##// END OF EJS Templates
Merge branch 'master' into alt_text
blois -
r26808:a09e5776 merge
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -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
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
The requested commit or file is too big and content was truncated. Show full diff
@@ -1,15 +1,21 b''
1 # When making commits that are strictly formatting/style changes, add the
1 # When making commits that are strictly formatting/style changes, add the
2 # commit hash here, so git blame can ignore the change. See docs for more
2 # commit hash here, so git blame can ignore the change. See docs for more
3 # details:
3 # details:
4 # https://git-scm.com/docs/git-config#Documentation/git-config.txt-blameignoreRevsFile
4 # https://git-scm.com/docs/git-config#Documentation/git-config.txt-blameignoreRevsFile
5 #
5 #
6 #
6 #
7 # You should be able to execute either
7 # You should be able to execute either
8 # ./tools/configure-git-blame-ignore-revs.bat or
8 # ./tools/configure-git-blame-ignore-revs.bat or
9 # ./tools/configure-git-blame-ignore-revs.sh
9 # ./tools/configure-git-blame-ignore-revs.sh
10 #
10 #
11 # Example entries:
11 # Example entries:
12 #
12 #
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
@@ -1,19 +1,16 b''
1 ---
1 ---
2 name: Bug report / Question / Feature
2 name: Bug report / Question / Feature
3 about: Anything related to IPython itsel
3 about: Anything related to IPython itsel
4 title: ''
4 title: ''
5 labels: ''
5 labels: ''
6 assignees: ''
6 assignees: ''
7
7
8 ---
8 ---
9
9
10 <!-- This is the repository for IPython command line, if you can try to make sure this question/bug/feature belong here and not on one of the Jupyter repositories.
10 <!-- This is the repository for IPython command line, if you can try to make sure this question/bug/feature belong here and not on one of the Jupyter repositories.
11
11
12 If it's a generic Python/Jupyter question, try other forums or discourse.jupyter.org.
12 If it's a generic Python/Jupyter question, try other forums or discourse.jupyter.org.
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 -->
@@ -1,33 +1,34 b''
1 name: Run MyPy
1 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:
11
11
12 runs-on: ubuntu-latest
12 runs-on: ubuntu-latest
13 strategy:
13 strategy:
14 matrix:
14 matrix:
15 python-version: [3.8]
15 python-version: [3.8]
16
16
17 steps:
17 steps:
18 - uses: actions/checkout@v2
18 - uses: actions/checkout@v2
19 - name: Set up Python ${{ matrix.python-version }}
19 - name: Set up Python ${{ matrix.python-version }}
20 uses: actions/setup-python@v2
20 uses: actions/setup-python@v2
21 with:
21 with:
22 python-version: ${{ matrix.python-version }}
22 python-version: ${{ matrix.python-version }}
23 - name: Install dependencies
23 - name: Install dependencies
24 run: |
24 run: |
25 python -m pip install --upgrade pip
25 python -m pip install --upgrade pip
26 pip install mypy pyflakes flake8
26 pip install mypy pyflakes flake8
27 - name: Lint with mypy
27 - name: Lint with mypy
28 run: |
28 run: |
29 mypy IPython/terminal/ptutils.py
29 mypy -p IPython.terminal
30 mypy IPython/core/c*.py
30 mypy -p IPython.core.magics
31 - name: Lint with pyflakes
31 - name: Lint with pyflakes
32 run: |
32 run: |
33 flake8 IPython/core/magics/script.py
33 flake8 IPython/core/magics/script.py
34 flake8 IPython/core/magics/packaging.py
@@ -1,39 +1,39 b''
1 # This workflow will install Python dependencies, run tests and lint with a variety of Python versions
1 # This workflow will install Python dependencies, run tests and lint with a variety of Python versions
2 # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
2 # For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
3
3
4 name: Python package
4 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:
14
14
15 runs-on: ubuntu-latest
15 runs-on: ubuntu-latest
16 strategy:
16 strategy:
17 matrix:
17 matrix:
18 python-version: [3.8]
18 python-version: [3.8]
19
19
20 steps:
20 steps:
21 - uses: actions/checkout@v2
21 - uses: actions/checkout@v2
22 with:
22 with:
23 fetch-depth: 0
23 fetch-depth: 0
24 - name: Set up Python ${{ matrix.python-version }}
24 - name: Set up Python ${{ matrix.python-version }}
25 uses: actions/setup-python@v2
25 uses: actions/setup-python@v2
26 with:
26 with:
27 python-version: ${{ matrix.python-version }}
27 python-version: ${{ matrix.python-version }}
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 . || (
35 echo "Changes need auto-formatting. Run:"
35 echo "Changes need auto-formatting. Run:"
36 echo " darker -r 60625f241f298b5039cb2debc365db38aa7bb522"
36 echo " darker -r 60625f241f298b5039cb2debc365db38aa7bb522"
37 echo "then commit and push changes to fix."
37 echo "then commit and push changes to fix."
38 exit 1
38 exit 1
39 )
39 )
@@ -1,30 +1,32 b''
1 MANIFEST
1 MANIFEST
2 build
2 build
3 dist
3 dist
4 _build
4 _build
5 docs/man/*.gz
5 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
12 jupyter_notebook/static/style/*.map
13 jupyter_notebook/static/style/*.map
13 *.py[co]
14 *.py[co]
14 __pycache__
15 __pycache__
15 *.egg-info
16 *.egg-info
16 *~
17 *~
17 *.bak
18 *.bak
18 .ipynb_checkpoints
19 .ipynb_checkpoints
19 .tox
20 .tox
20 .DS_Store
21 .DS_Store
21 \#*#
22 \#*#
22 .#*
23 .#*
23 .cache
24 .cache
24 .coverage
25 .coverage
25 *.swp
26 *.swp
26 .vscode
27 .vscode
27 .pytest_cache
28 .pytest_cache
28 .python-version
29 .python-version
29 venv*/
30 venv*/
30 .idea/
31 .idea/
32 .mypy_cache/
@@ -1,152 +1,149 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 IPython: tools for interactive and parallel computing in Python.
3 IPython: tools for interactive and parallel computing in Python.
4
4
5 https://ipython.org
5 https://ipython.org
6 """
6 """
7 #-----------------------------------------------------------------------------
7 #-----------------------------------------------------------------------------
8 # Copyright (c) 2008-2011, IPython Development Team.
8 # Copyright (c) 2008-2011, IPython Development Team.
9 # Copyright (c) 2001-2007, Fernando Perez <fernando.perez@colorado.edu>
9 # Copyright (c) 2001-2007, Fernando Perez <fernando.perez@colorado.edu>
10 # Copyright (c) 2001, Janko Hauser <jhauser@zscout.de>
10 # Copyright (c) 2001, Janko Hauser <jhauser@zscout.de>
11 # Copyright (c) 2001, Nathaniel Gray <n8gray@caltech.edu>
11 # Copyright (c) 2001, Nathaniel Gray <n8gray@caltech.edu>
12 #
12 #
13 # Distributed under the terms of the Modified BSD License.
13 # Distributed under the terms of the Modified BSD License.
14 #
14 #
15 # The full license is in the file COPYING.txt, distributed with this software.
15 # The full license is in the file COPYING.txt, distributed with this software.
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 # Imports
19 # Imports
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22 import os
22 import os
23 import sys
23 import sys
24
24
25 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
26 # Setup everything
26 # Setup everything
27 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
28
28
29 # Don't forget to also update setup.py when this changes!
29 # Don't forget to also update setup.py when this changes!
30 if sys.version_info < (3, 6):
30 if sys.version_info < (3, 6):
31 raise ImportError(
31 raise ImportError(
32 """
32 """
33 IPython 7.10+ supports Python 3.6 and above.
33 IPython 7.10+ supports Python 3.6 and above.
34 When using Python 2.7, please install IPython 5.x LTS Long Term Support version.
34 When using Python 2.7, please install IPython 5.x LTS Long Term Support version.
35 Python 3.3 and 3.4 were supported up to IPython 6.x.
35 Python 3.3 and 3.4 were supported up to IPython 6.x.
36 Python 3.5 was supported with IPython 7.0 to 7.9.
36 Python 3.5 was supported with IPython 7.0 to 7.9.
37
37
38 See IPython `README.rst` file for more information:
38 See IPython `README.rst` file for more information:
39
39
40 https://github.com/ipython/ipython/blob/master/README.rst
40 https://github.com/ipython/ipython/blob/master/README.rst
41
41
42 """)
42 """)
43
43
44 # Make it easy to import extensions - they are always directly on pythonpath.
44 # Make it easy to import extensions - they are always directly on pythonpath.
45 # Therefore, non-IPython modules can be added to extensions directory.
45 # Therefore, non-IPython modules can be added to extensions directory.
46 # This should probably be in ipapp.py.
46 # This should probably be in ipapp.py.
47 sys.path.append(os.path.join(os.path.dirname(__file__), "extensions"))
47 sys.path.append(os.path.join(os.path.dirname(__file__), "extensions"))
48
48
49 #-----------------------------------------------------------------------------
49 #-----------------------------------------------------------------------------
50 # Setup the top level names
50 # Setup the top level names
51 #-----------------------------------------------------------------------------
51 #-----------------------------------------------------------------------------
52
52
53 from .core.getipython import get_ipython
53 from .core.getipython import get_ipython
54 from .core import release
54 from .core import release
55 from .core.application import Application
55 from .core.application import Application
56 from .terminal.embed import embed
56 from .terminal.embed import embed
57
57
58 from .core.interactiveshell import InteractiveShell
58 from .core.interactiveshell import InteractiveShell
59 from .testing import test
59 from .testing import test
60 from .utils.sysinfo import sys_info
60 from .utils.sysinfo import sys_info
61 from .utils.frame import extract_module_locals
61 from .utils.frame import extract_module_locals
62
62
63 # Release data
63 # Release data
64 __author__ = '%s <%s>' % (release.author, release.author_email)
64 __author__ = '%s <%s>' % (release.author, release.author_email)
65 __license__ = release.license
65 __license__ = release.license
66 __version__ = release.version
66 __version__ = release.version
67 version_info = release.version_info
67 version_info = release.version_info
68
68
69 def embed_kernel(module=None, local_ns=None, **kwargs):
69 def embed_kernel(module=None, local_ns=None, **kwargs):
70 """Embed and start an IPython kernel in a given scope.
70 """Embed and start an IPython kernel in a given scope.
71
71
72 If you don't want the kernel to initialize the namespace
72 If you don't want the kernel to initialize the namespace
73 from the scope of the surrounding function,
73 from the scope of the surrounding function,
74 and/or you want to load full IPython configuration,
74 and/or you want to load full IPython configuration,
75 you probably want `IPython.start_kernel()` instead.
75 you probably want `IPython.start_kernel()` instead.
76
76
77 Parameters
77 Parameters
78 ----------
78 ----------
79 module : types.ModuleType, optional
79 module : types.ModuleType, optional
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.
88 """
87 """
89
88
90 (caller_module, caller_locals) = extract_module_locals(1)
89 (caller_module, caller_locals) = extract_module_locals(1)
91 if module is None:
90 if module is None:
92 module = caller_module
91 module = caller_module
93 if local_ns is None:
92 if local_ns is None:
94 local_ns = caller_locals
93 local_ns = caller_locals
95
94
96 # Only import .zmq when we really need it
95 # Only import .zmq when we really need it
97 from ipykernel.embed import embed_kernel as real_embed_kernel
96 from ipykernel.embed import embed_kernel as real_embed_kernel
98 real_embed_kernel(module=module, local_ns=local_ns, **kwargs)
97 real_embed_kernel(module=module, local_ns=local_ns, **kwargs)
99
98
100 def start_ipython(argv=None, **kwargs):
99 def start_ipython(argv=None, **kwargs):
101 """Launch a normal IPython instance (as opposed to embedded)
100 """Launch a normal IPython instance (as opposed to embedded)
102
101
103 `IPython.embed()` puts a shell in a particular calling scope,
102 `IPython.embed()` puts a shell in a particular calling scope,
104 such as a function or method for debugging purposes,
103 such as a function or method for debugging purposes,
105 which is often not desirable.
104 which is often not desirable.
106
105
107 `start_ipython()` does full, regular IPython initialization,
106 `start_ipython()` does full, regular IPython initialization,
108 including loading startup files, configuration, etc.
107 including loading startup files, configuration, etc.
109 much of which is skipped by `embed()`.
108 much of which is skipped by `embed()`.
110
109
111 This is a public API method, and will survive implementation changes.
110 This is a public API method, and will survive implementation changes.
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 """
125 from IPython.terminal.ipapp import launch_new_instance
123 from IPython.terminal.ipapp import launch_new_instance
126 return launch_new_instance(argv=argv, **kwargs)
124 return launch_new_instance(argv=argv, **kwargs)
127
125
128 def start_kernel(argv=None, **kwargs):
126 def start_kernel(argv=None, **kwargs):
129 """Launch a normal IPython kernel instance (as opposed to embedded)
127 """Launch a normal IPython kernel instance (as opposed to embedded)
130
128
131 `IPython.embed_kernel()` puts a shell in a particular calling scope,
129 `IPython.embed_kernel()` puts a shell in a particular calling scope,
132 such as a function or method for debugging purposes,
130 such as a function or method for debugging purposes,
133 which is often not desirable.
131 which is often not desirable.
134
132
135 `start_kernel()` does full, regular IPython initialization,
133 `start_kernel()` does full, regular IPython initialization,
136 including loading startup files, configuration, etc.
134 including loading startup files, configuration, etc.
137 much of which is skipped by `embed()`.
135 much of which is skipped by `embed()`.
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 """
151 from IPython.kernel.zmq.kernelapp import launch_new_instance
148 from IPython.kernel.zmq.kernelapp import launch_new_instance
152 return launch_new_instance(argv=argv, **kwargs)
149 return launch_new_instance(argv=argv, **kwargs)
@@ -1,464 +1,486 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 An application for IPython.
3 An application for IPython.
4
4
5 All top-level applications should use the classes in this module for
5 All top-level applications should use the classes in this module for
6 handling configuration and creating configurables.
6 handling configuration and creating configurables.
7
7
8 The job of an :class:`Application` is to create the master configuration
8 The job of an :class:`Application` is to create the master configuration
9 object and then create the configurable objects, passing the config to them.
9 object and then create the configurable objects, passing the config to them.
10 """
10 """
11
11
12 # Copyright (c) IPython Development Team.
12 # Copyright (c) IPython Development Team.
13 # Distributed under the terms of the Modified BSD License.
13 # Distributed under the terms of the Modified BSD License.
14
14
15 import atexit
15 import atexit
16 from copy import deepcopy
16 from copy import deepcopy
17 import glob
17 import glob
18 import logging
18 import logging
19 import os
19 import os
20 import shutil
20 import shutil
21 import sys
21 import sys
22
22
23 from pathlib import Path
23 from pathlib import Path
24
24
25 from traitlets.config.application import Application, catch_config_error
25 from traitlets.config.application import Application, catch_config_error
26 from traitlets.config.loader import ConfigFileNotFound, PyFileConfigLoader
26 from traitlets.config.loader import ConfigFileNotFound, PyFileConfigLoader
27 from IPython.core import release, crashhandler
27 from IPython.core import release, crashhandler
28 from IPython.core.profiledir import ProfileDir, ProfileDirError
28 from IPython.core.profiledir import ProfileDir, ProfileDirError
29 from IPython.paths import get_ipython_dir, get_ipython_package_dir
29 from IPython.paths import get_ipython_dir, get_ipython_package_dir
30 from IPython.utils.path import ensure_dir_exists
30 from IPython.utils.path import ensure_dir_exists
31 from traitlets import (
31 from traitlets import (
32 List, Unicode, Type, Bool, Set, Instance, Undefined,
32 List, Unicode, Type, Bool, Set, Instance, Undefined,
33 default, observe,
33 default, observe,
34 )
34 )
35
35
36 if os.name == "nt":
36 if os.name == "nt":
37 programdata = Path(os.environ.get("PROGRAMDATA", None))
37 programdata = Path(os.environ.get("PROGRAMDATA", None))
38 if programdata:
38 if programdata:
39 SYSTEM_CONFIG_DIRS = [str(programdata / "ipython")]
39 SYSTEM_CONFIG_DIRS = [str(programdata / "ipython")]
40 else: # PROGRAMDATA is not defined by default on XP.
40 else: # PROGRAMDATA is not defined by default on XP.
41 SYSTEM_CONFIG_DIRS = []
41 SYSTEM_CONFIG_DIRS = []
42 else:
42 else:
43 SYSTEM_CONFIG_DIRS = [
43 SYSTEM_CONFIG_DIRS = [
44 "/usr/local/etc/ipython",
44 "/usr/local/etc/ipython",
45 "/etc/ipython",
45 "/etc/ipython",
46 ]
46 ]
47
47
48
48
49 ENV_CONFIG_DIRS = []
49 ENV_CONFIG_DIRS = []
50 _env_config_dir = os.path.join(sys.prefix, 'etc', 'ipython')
50 _env_config_dir = os.path.join(sys.prefix, 'etc', 'ipython')
51 if _env_config_dir not in SYSTEM_CONFIG_DIRS:
51 if _env_config_dir not in SYSTEM_CONFIG_DIRS:
52 # only add ENV_CONFIG if sys.prefix is not already included
52 # only add ENV_CONFIG if sys.prefix is not already included
53 ENV_CONFIG_DIRS.append(_env_config_dir)
53 ENV_CONFIG_DIRS.append(_env_config_dir)
54
54
55
55
56 _envvar = os.environ.get('IPYTHON_SUPPRESS_CONFIG_ERRORS')
56 _envvar = os.environ.get('IPYTHON_SUPPRESS_CONFIG_ERRORS')
57 if _envvar in {None, ''}:
57 if _envvar in {None, ''}:
58 IPYTHON_SUPPRESS_CONFIG_ERRORS = None
58 IPYTHON_SUPPRESS_CONFIG_ERRORS = None
59 else:
59 else:
60 if _envvar.lower() in {'1','true'}:
60 if _envvar.lower() in {'1','true'}:
61 IPYTHON_SUPPRESS_CONFIG_ERRORS = True
61 IPYTHON_SUPPRESS_CONFIG_ERRORS = True
62 elif _envvar.lower() in {'0','false'} :
62 elif _envvar.lower() in {'0','false'} :
63 IPYTHON_SUPPRESS_CONFIG_ERRORS = False
63 IPYTHON_SUPPRESS_CONFIG_ERRORS = False
64 else:
64 else:
65 sys.exit("Unsupported value for environment variable: 'IPYTHON_SUPPRESS_CONFIG_ERRORS' is set to '%s' which is none of {'0', '1', 'false', 'true', ''}."% _envvar )
65 sys.exit("Unsupported value for environment variable: 'IPYTHON_SUPPRESS_CONFIG_ERRORS' is set to '%s' which is none of {'0', '1', 'false', 'true', ''}."% _envvar )
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 }
75 "profile-dir": "ProfileDir.location",
76
76 "profile": "BaseIPythonApplication.profile",
77 base_flags = dict(
77 "ipython-dir": "BaseIPythonApplication.ipython_dir",
78 debug = ({'Application' : {'log_level' : logging.DEBUG}},
78 "log-level": "Application.log_level",
79 "set log level to logging.DEBUG (maximize logging output)"),
79 "config": "BaseIPythonApplication.extra_config_file",
80 quiet = ({'Application' : {'log_level' : logging.CRITICAL}},
80 }
81 "set log level to logging.CRITICAL (minimize logging output)"),
81 )
82 init = ({'BaseIPythonApplication' : {
82
83 'copy_config_files' : True,
83 base_flags = dict()
84 'auto_create' : True}
84 if isinstance(Application.flags, dict):
85 }, """Initialize profile with default config files. This is equivalent
85 # traitlets 5
86 base_flags.update(Application.flags)
87 base_flags.update(
88 dict(
89 debug=(
90 {"Application": {"log_level": logging.DEBUG}},
91 "set log level to logging.DEBUG (maximize logging output)",
92 ),
93 quiet=(
94 {"Application": {"log_level": logging.CRITICAL}},
95 "set log level to logging.CRITICAL (minimize logging output)",
96 ),
97 init=(
98 {
99 "BaseIPythonApplication": {
100 "copy_config_files": True,
101 "auto_create": True,
102 }
103 },
104 """Initialize profile with default config files. This is equivalent
86 to running `ipython profile create <profile>` prior to startup.
105 to running `ipython profile create <profile>` prior to startup.
87 """)
106 """,
107 ),
108 )
88 )
109 )
89
110
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."""
92 def load_subconfig(self, fname, path=None, profile=None):
114 def load_subconfig(self, fname, path=None, profile=None):
93 if profile is not None:
115 if profile is not None:
94 try:
116 try:
95 profile_dir = ProfileDir.find_profile_dir_by_name(
117 profile_dir = ProfileDir.find_profile_dir_by_name(
96 get_ipython_dir(),
118 get_ipython_dir(),
97 profile,
119 profile,
98 )
120 )
99 except ProfileDirError:
121 except ProfileDirError:
100 return
122 return
101 path = profile_dir.location
123 path = profile_dir.location
102 return super(ProfileAwareConfigLoader, self).load_subconfig(fname, path=path)
124 return super(ProfileAwareConfigLoader, self).load_subconfig(fname, path=path)
103
125
104 class BaseIPythonApplication(Application):
126 class BaseIPythonApplication(Application):
105
127
106 name = u'ipython'
128 name = u'ipython'
107 description = Unicode(u'IPython: an enhanced interactive Python shell.')
129 description = Unicode(u'IPython: an enhanced interactive Python shell.')
108 version = Unicode(release.version)
130 version = Unicode(release.version)
109
131
110 aliases = base_aliases
132 aliases = base_aliases
111 flags = base_flags
133 flags = base_flags
112 classes = List([ProfileDir])
134 classes = List([ProfileDir])
113
135
114 # enable `load_subconfig('cfg.py', profile='name')`
136 # enable `load_subconfig('cfg.py', profile='name')`
115 python_config_loader_class = ProfileAwareConfigLoader
137 python_config_loader_class = ProfileAwareConfigLoader
116
138
117 # Track whether the config_file has changed,
139 # Track whether the config_file has changed,
118 # because some logic happens only if we aren't using the default.
140 # because some logic happens only if we aren't using the default.
119 config_file_specified = Set()
141 config_file_specified = Set()
120
142
121 config_file_name = Unicode()
143 config_file_name = Unicode()
122 @default('config_file_name')
144 @default('config_file_name')
123 def _config_file_name_default(self):
145 def _config_file_name_default(self):
124 return self.name.replace('-','_') + u'_config.py'
146 return self.name.replace('-','_') + u'_config.py'
125 @observe('config_file_name')
147 @observe('config_file_name')
126 def _config_file_name_changed(self, change):
148 def _config_file_name_changed(self, change):
127 if change['new'] != change['old']:
149 if change['new'] != change['old']:
128 self.config_file_specified.add(change['new'])
150 self.config_file_specified.add(change['new'])
129
151
130 # The directory that contains IPython's builtin profiles.
152 # The directory that contains IPython's builtin profiles.
131 builtin_profile_dir = Unicode(
153 builtin_profile_dir = Unicode(
132 os.path.join(get_ipython_package_dir(), u'config', u'profile', u'default')
154 os.path.join(get_ipython_package_dir(), u'config', u'profile', u'default')
133 )
155 )
134
156
135 config_file_paths = List(Unicode())
157 config_file_paths = List(Unicode())
136 @default('config_file_paths')
158 @default('config_file_paths')
137 def _config_file_paths_default(self):
159 def _config_file_paths_default(self):
138 return [os.getcwd()]
160 return [os.getcwd()]
139
161
140 extra_config_file = Unicode(
162 extra_config_file = Unicode(
141 help="""Path to an extra config file to load.
163 help="""Path to an extra config file to load.
142
164
143 If specified, load this config file in addition to any other IPython config.
165 If specified, load this config file in addition to any other IPython config.
144 """).tag(config=True)
166 """).tag(config=True)
145 @observe('extra_config_file')
167 @observe('extra_config_file')
146 def _extra_config_file_changed(self, change):
168 def _extra_config_file_changed(self, change):
147 old = change['old']
169 old = change['old']
148 new = change['new']
170 new = change['new']
149 try:
171 try:
150 self.config_files.remove(old)
172 self.config_files.remove(old)
151 except ValueError:
173 except ValueError:
152 pass
174 pass
153 self.config_file_specified.add(new)
175 self.config_file_specified.add(new)
154 self.config_files.append(new)
176 self.config_files.append(new)
155
177
156 profile = Unicode(u'default',
178 profile = Unicode(u'default',
157 help="""The IPython profile to use."""
179 help="""The IPython profile to use."""
158 ).tag(config=True)
180 ).tag(config=True)
159
181
160 @observe('profile')
182 @observe('profile')
161 def _profile_changed(self, change):
183 def _profile_changed(self, change):
162 self.builtin_profile_dir = os.path.join(
184 self.builtin_profile_dir = os.path.join(
163 get_ipython_package_dir(), u'config', u'profile', change['new']
185 get_ipython_package_dir(), u'config', u'profile', change['new']
164 )
186 )
165
187
166 ipython_dir = Unicode(
188 ipython_dir = Unicode(
167 help="""
189 help="""
168 The name of the IPython directory. This directory is used for logging
190 The name of the IPython directory. This directory is used for logging
169 configuration (through profiles), history storage, etc. The default
191 configuration (through profiles), history storage, etc. The default
170 is usually $HOME/.ipython. This option can also be specified through
192 is usually $HOME/.ipython. This option can also be specified through
171 the environment variable IPYTHONDIR.
193 the environment variable IPYTHONDIR.
172 """
194 """
173 ).tag(config=True)
195 ).tag(config=True)
174 @default('ipython_dir')
196 @default('ipython_dir')
175 def _ipython_dir_default(self):
197 def _ipython_dir_default(self):
176 d = get_ipython_dir()
198 d = get_ipython_dir()
177 self._ipython_dir_changed({
199 self._ipython_dir_changed({
178 'name': 'ipython_dir',
200 'name': 'ipython_dir',
179 'old': d,
201 'old': d,
180 'new': d,
202 'new': d,
181 })
203 })
182 return d
204 return d
183
205
184 _in_init_profile_dir = False
206 _in_init_profile_dir = False
185 profile_dir = Instance(ProfileDir, allow_none=True)
207 profile_dir = Instance(ProfileDir, allow_none=True)
186 @default('profile_dir')
208 @default('profile_dir')
187 def _profile_dir_default(self):
209 def _profile_dir_default(self):
188 # avoid recursion
210 # avoid recursion
189 if self._in_init_profile_dir:
211 if self._in_init_profile_dir:
190 return
212 return
191 # profile_dir requested early, force initialization
213 # profile_dir requested early, force initialization
192 self.init_profile_dir()
214 self.init_profile_dir()
193 return self.profile_dir
215 return self.profile_dir
194
216
195 overwrite = Bool(False,
217 overwrite = Bool(False,
196 help="""Whether to overwrite existing config files when copying"""
218 help="""Whether to overwrite existing config files when copying"""
197 ).tag(config=True)
219 ).tag(config=True)
198 auto_create = Bool(False,
220 auto_create = Bool(False,
199 help="""Whether to create profile dir if it doesn't exist"""
221 help="""Whether to create profile dir if it doesn't exist"""
200 ).tag(config=True)
222 ).tag(config=True)
201
223
202 config_files = List(Unicode())
224 config_files = List(Unicode())
203 @default('config_files')
225 @default('config_files')
204 def _config_files_default(self):
226 def _config_files_default(self):
205 return [self.config_file_name]
227 return [self.config_file_name]
206
228
207 copy_config_files = Bool(False,
229 copy_config_files = Bool(False,
208 help="""Whether to install the default config files into the profile dir.
230 help="""Whether to install the default config files into the profile dir.
209 If a new profile is being created, and IPython contains config files for that
231 If a new profile is being created, and IPython contains config files for that
210 profile, then they will be staged into the new directory. Otherwise,
232 profile, then they will be staged into the new directory. Otherwise,
211 default config files will be automatically generated.
233 default config files will be automatically generated.
212 """).tag(config=True)
234 """).tag(config=True)
213
235
214 verbose_crash = Bool(False,
236 verbose_crash = Bool(False,
215 help="""Create a massive crash report when IPython encounters what may be an
237 help="""Create a massive crash report when IPython encounters what may be an
216 internal error. The default is to append a short message to the
238 internal error. The default is to append a short message to the
217 usual traceback""").tag(config=True)
239 usual traceback""").tag(config=True)
218
240
219 # The class to use as the crash handler.
241 # The class to use as the crash handler.
220 crash_handler_class = Type(crashhandler.CrashHandler)
242 crash_handler_class = Type(crashhandler.CrashHandler)
221
243
222 @catch_config_error
244 @catch_config_error
223 def __init__(self, **kwargs):
245 def __init__(self, **kwargs):
224 super(BaseIPythonApplication, self).__init__(**kwargs)
246 super(BaseIPythonApplication, self).__init__(**kwargs)
225 # ensure current working directory exists
247 # ensure current working directory exists
226 try:
248 try:
227 os.getcwd()
249 os.getcwd()
228 except:
250 except:
229 # exit if cwd doesn't exist
251 # exit if cwd doesn't exist
230 self.log.error("Current working directory doesn't exist.")
252 self.log.error("Current working directory doesn't exist.")
231 self.exit(1)
253 self.exit(1)
232
254
233 #-------------------------------------------------------------------------
255 #-------------------------------------------------------------------------
234 # Various stages of Application creation
256 # Various stages of Application creation
235 #-------------------------------------------------------------------------
257 #-------------------------------------------------------------------------
236
258
237 deprecated_subcommands = {}
259 deprecated_subcommands = {}
238
260
239 def initialize_subcommand(self, subc, argv=None):
261 def initialize_subcommand(self, subc, argv=None):
240 if subc in self.deprecated_subcommands:
262 if subc in self.deprecated_subcommands:
241 self.log.warning("Subcommand `ipython {sub}` is deprecated and will be removed "
263 self.log.warning("Subcommand `ipython {sub}` is deprecated and will be removed "
242 "in future versions.".format(sub=subc))
264 "in future versions.".format(sub=subc))
243 self.log.warning("You likely want to use `jupyter {sub}` in the "
265 self.log.warning("You likely want to use `jupyter {sub}` in the "
244 "future".format(sub=subc))
266 "future".format(sub=subc))
245 return super(BaseIPythonApplication, self).initialize_subcommand(subc, argv)
267 return super(BaseIPythonApplication, self).initialize_subcommand(subc, argv)
246
268
247 def init_crash_handler(self):
269 def init_crash_handler(self):
248 """Create a crash handler, typically setting sys.excepthook to it."""
270 """Create a crash handler, typically setting sys.excepthook to it."""
249 self.crash_handler = self.crash_handler_class(self)
271 self.crash_handler = self.crash_handler_class(self)
250 sys.excepthook = self.excepthook
272 sys.excepthook = self.excepthook
251 def unset_crashhandler():
273 def unset_crashhandler():
252 sys.excepthook = sys.__excepthook__
274 sys.excepthook = sys.__excepthook__
253 atexit.register(unset_crashhandler)
275 atexit.register(unset_crashhandler)
254
276
255 def excepthook(self, etype, evalue, tb):
277 def excepthook(self, etype, evalue, tb):
256 """this is sys.excepthook after init_crashhandler
278 """this is sys.excepthook after init_crashhandler
257
279
258 set self.verbose_crash=True to use our full crashhandler, instead of
280 set self.verbose_crash=True to use our full crashhandler, instead of
259 a regular traceback with a short message (crash_handler_lite)
281 a regular traceback with a short message (crash_handler_lite)
260 """
282 """
261
283
262 if self.verbose_crash:
284 if self.verbose_crash:
263 return self.crash_handler(etype, evalue, tb)
285 return self.crash_handler(etype, evalue, tb)
264 else:
286 else:
265 return crashhandler.crash_handler_lite(etype, evalue, tb)
287 return crashhandler.crash_handler_lite(etype, evalue, tb)
266
288
267 @observe('ipython_dir')
289 @observe('ipython_dir')
268 def _ipython_dir_changed(self, change):
290 def _ipython_dir_changed(self, change):
269 old = change['old']
291 old = change['old']
270 new = change['new']
292 new = change['new']
271 if old is not Undefined:
293 if old is not Undefined:
272 str_old = os.path.abspath(old)
294 str_old = os.path.abspath(old)
273 if str_old in sys.path:
295 if str_old in sys.path:
274 sys.path.remove(str_old)
296 sys.path.remove(str_old)
275 str_path = os.path.abspath(new)
297 str_path = os.path.abspath(new)
276 sys.path.append(str_path)
298 sys.path.append(str_path)
277 ensure_dir_exists(new)
299 ensure_dir_exists(new)
278 readme = os.path.join(new, 'README')
300 readme = os.path.join(new, 'README')
279 readme_src = os.path.join(get_ipython_package_dir(), u'config', u'profile', 'README')
301 readme_src = os.path.join(get_ipython_package_dir(), u'config', u'profile', 'README')
280 if not os.path.exists(readme) and os.path.exists(readme_src):
302 if not os.path.exists(readme) and os.path.exists(readme_src):
281 shutil.copy(readme_src, readme)
303 shutil.copy(readme_src, readme)
282 for d in ('extensions', 'nbextensions'):
304 for d in ('extensions', 'nbextensions'):
283 path = os.path.join(new, d)
305 path = os.path.join(new, d)
284 try:
306 try:
285 ensure_dir_exists(path)
307 ensure_dir_exists(path)
286 except OSError as e:
308 except OSError as e:
287 # this will not be EEXIST
309 # this will not be EEXIST
288 self.log.error("couldn't create path %s: %s", path, e)
310 self.log.error("couldn't create path %s: %s", path, e)
289 self.log.debug("IPYTHONDIR set to: %s" % new)
311 self.log.debug("IPYTHONDIR set to: %s" % new)
290
312
291 def load_config_file(self, suppress_errors=IPYTHON_SUPPRESS_CONFIG_ERRORS):
313 def load_config_file(self, suppress_errors=IPYTHON_SUPPRESS_CONFIG_ERRORS):
292 """Load the config file.
314 """Load the config file.
293
315
294 By default, errors in loading config are handled, and a warning
316 By default, errors in loading config are handled, and a warning
295 printed on screen. For testing, the suppress_errors option is set
317 printed on screen. For testing, the suppress_errors option is set
296 to False, so errors will make tests fail.
318 to False, so errors will make tests fail.
297
319
298 `suppress_errors` default value is to be `None` in which case the
320 `suppress_errors` default value is to be `None` in which case the
299 behavior default to the one of `traitlets.Application`.
321 behavior default to the one of `traitlets.Application`.
300
322
301 The default value can be set :
323 The default value can be set :
302 - to `False` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '0', or 'false' (case insensitive).
324 - to `False` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '0', or 'false' (case insensitive).
303 - to `True` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '1' or 'true' (case insensitive).
325 - to `True` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '1' or 'true' (case insensitive).
304 - to `None` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '' (empty string) or leaving it unset.
326 - to `None` by setting 'IPYTHON_SUPPRESS_CONFIG_ERRORS' environment variable to '' (empty string) or leaving it unset.
305
327
306 Any other value are invalid, and will make IPython exit with a non-zero return code.
328 Any other value are invalid, and will make IPython exit with a non-zero return code.
307 """
329 """
308
330
309
331
310 self.log.debug("Searching path %s for config files", self.config_file_paths)
332 self.log.debug("Searching path %s for config files", self.config_file_paths)
311 base_config = 'ipython_config.py'
333 base_config = 'ipython_config.py'
312 self.log.debug("Attempting to load config file: %s" %
334 self.log.debug("Attempting to load config file: %s" %
313 base_config)
335 base_config)
314 try:
336 try:
315 if suppress_errors is not None:
337 if suppress_errors is not None:
316 old_value = Application.raise_config_file_errors
338 old_value = Application.raise_config_file_errors
317 Application.raise_config_file_errors = not suppress_errors;
339 Application.raise_config_file_errors = not suppress_errors;
318 Application.load_config_file(
340 Application.load_config_file(
319 self,
341 self,
320 base_config,
342 base_config,
321 path=self.config_file_paths
343 path=self.config_file_paths
322 )
344 )
323 except ConfigFileNotFound:
345 except ConfigFileNotFound:
324 # ignore errors loading parent
346 # ignore errors loading parent
325 self.log.debug("Config file %s not found", base_config)
347 self.log.debug("Config file %s not found", base_config)
326 pass
348 pass
327 if suppress_errors is not None:
349 if suppress_errors is not None:
328 Application.raise_config_file_errors = old_value
350 Application.raise_config_file_errors = old_value
329
351
330 for config_file_name in self.config_files:
352 for config_file_name in self.config_files:
331 if not config_file_name or config_file_name == base_config:
353 if not config_file_name or config_file_name == base_config:
332 continue
354 continue
333 self.log.debug("Attempting to load config file: %s" %
355 self.log.debug("Attempting to load config file: %s" %
334 self.config_file_name)
356 self.config_file_name)
335 try:
357 try:
336 Application.load_config_file(
358 Application.load_config_file(
337 self,
359 self,
338 config_file_name,
360 config_file_name,
339 path=self.config_file_paths
361 path=self.config_file_paths
340 )
362 )
341 except ConfigFileNotFound:
363 except ConfigFileNotFound:
342 # Only warn if the default config file was NOT being used.
364 # Only warn if the default config file was NOT being used.
343 if config_file_name in self.config_file_specified:
365 if config_file_name in self.config_file_specified:
344 msg = self.log.warning
366 msg = self.log.warning
345 else:
367 else:
346 msg = self.log.debug
368 msg = self.log.debug
347 msg("Config file not found, skipping: %s", config_file_name)
369 msg("Config file not found, skipping: %s", config_file_name)
348 except Exception:
370 except Exception:
349 # For testing purposes.
371 # For testing purposes.
350 if not suppress_errors:
372 if not suppress_errors:
351 raise
373 raise
352 self.log.warning("Error loading config file: %s" %
374 self.log.warning("Error loading config file: %s" %
353 self.config_file_name, exc_info=True)
375 self.config_file_name, exc_info=True)
354
376
355 def init_profile_dir(self):
377 def init_profile_dir(self):
356 """initialize the profile dir"""
378 """initialize the profile dir"""
357 self._in_init_profile_dir = True
379 self._in_init_profile_dir = True
358 if self.profile_dir is not None:
380 if self.profile_dir is not None:
359 # already ran
381 # already ran
360 return
382 return
361 if 'ProfileDir.location' not in self.config:
383 if 'ProfileDir.location' not in self.config:
362 # location not specified, find by profile name
384 # location not specified, find by profile name
363 try:
385 try:
364 p = ProfileDir.find_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
386 p = ProfileDir.find_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
365 except ProfileDirError:
387 except ProfileDirError:
366 # not found, maybe create it (always create default profile)
388 # not found, maybe create it (always create default profile)
367 if self.auto_create or self.profile == 'default':
389 if self.auto_create or self.profile == 'default':
368 try:
390 try:
369 p = ProfileDir.create_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
391 p = ProfileDir.create_profile_dir_by_name(self.ipython_dir, self.profile, self.config)
370 except ProfileDirError:
392 except ProfileDirError:
371 self.log.fatal("Could not create profile: %r"%self.profile)
393 self.log.fatal("Could not create profile: %r"%self.profile)
372 self.exit(1)
394 self.exit(1)
373 else:
395 else:
374 self.log.info("Created profile dir: %r"%p.location)
396 self.log.info("Created profile dir: %r"%p.location)
375 else:
397 else:
376 self.log.fatal("Profile %r not found."%self.profile)
398 self.log.fatal("Profile %r not found."%self.profile)
377 self.exit(1)
399 self.exit(1)
378 else:
400 else:
379 self.log.debug("Using existing profile dir: %r"%p.location)
401 self.log.debug("Using existing profile dir: %r"%p.location)
380 else:
402 else:
381 location = self.config.ProfileDir.location
403 location = self.config.ProfileDir.location
382 # location is fully specified
404 # location is fully specified
383 try:
405 try:
384 p = ProfileDir.find_profile_dir(location, self.config)
406 p = ProfileDir.find_profile_dir(location, self.config)
385 except ProfileDirError:
407 except ProfileDirError:
386 # not found, maybe create it
408 # not found, maybe create it
387 if self.auto_create:
409 if self.auto_create:
388 try:
410 try:
389 p = ProfileDir.create_profile_dir(location, self.config)
411 p = ProfileDir.create_profile_dir(location, self.config)
390 except ProfileDirError:
412 except ProfileDirError:
391 self.log.fatal("Could not create profile directory: %r"%location)
413 self.log.fatal("Could not create profile directory: %r"%location)
392 self.exit(1)
414 self.exit(1)
393 else:
415 else:
394 self.log.debug("Creating new profile dir: %r"%location)
416 self.log.debug("Creating new profile dir: %r"%location)
395 else:
417 else:
396 self.log.fatal("Profile directory %r not found."%location)
418 self.log.fatal("Profile directory %r not found."%location)
397 self.exit(1)
419 self.exit(1)
398 else:
420 else:
399 self.log.info("Using existing profile dir: %r"%location)
421 self.log.info("Using existing profile dir: %r"%location)
400 # if profile_dir is specified explicitly, set profile name
422 # if profile_dir is specified explicitly, set profile name
401 dir_name = os.path.basename(p.location)
423 dir_name = os.path.basename(p.location)
402 if dir_name.startswith('profile_'):
424 if dir_name.startswith('profile_'):
403 self.profile = dir_name[8:]
425 self.profile = dir_name[8:]
404
426
405 self.profile_dir = p
427 self.profile_dir = p
406 self.config_file_paths.append(p.location)
428 self.config_file_paths.append(p.location)
407 self._in_init_profile_dir = False
429 self._in_init_profile_dir = False
408
430
409 def init_config_files(self):
431 def init_config_files(self):
410 """[optionally] copy default config files into profile dir."""
432 """[optionally] copy default config files into profile dir."""
411 self.config_file_paths.extend(ENV_CONFIG_DIRS)
433 self.config_file_paths.extend(ENV_CONFIG_DIRS)
412 self.config_file_paths.extend(SYSTEM_CONFIG_DIRS)
434 self.config_file_paths.extend(SYSTEM_CONFIG_DIRS)
413 # copy config files
435 # copy config files
414 path = Path(self.builtin_profile_dir)
436 path = Path(self.builtin_profile_dir)
415 if self.copy_config_files:
437 if self.copy_config_files:
416 src = self.profile
438 src = self.profile
417
439
418 cfg = self.config_file_name
440 cfg = self.config_file_name
419 if path and (path / cfg).exists():
441 if path and (path / cfg).exists():
420 self.log.warning(
442 self.log.warning(
421 "Staging %r from %s into %r [overwrite=%s]"
443 "Staging %r from %s into %r [overwrite=%s]"
422 % (cfg, src, self.profile_dir.location, self.overwrite)
444 % (cfg, src, self.profile_dir.location, self.overwrite)
423 )
445 )
424 self.profile_dir.copy_config_file(cfg, path=path, overwrite=self.overwrite)
446 self.profile_dir.copy_config_file(cfg, path=path, overwrite=self.overwrite)
425 else:
447 else:
426 self.stage_default_config_file()
448 self.stage_default_config_file()
427 else:
449 else:
428 # Still stage *bundled* config files, but not generated ones
450 # Still stage *bundled* config files, but not generated ones
429 # This is necessary for `ipython profile=sympy` to load the profile
451 # This is necessary for `ipython profile=sympy` to load the profile
430 # on the first go
452 # on the first go
431 files = path.glob("*.py")
453 files = path.glob("*.py")
432 for fullpath in files:
454 for fullpath in files:
433 cfg = fullpath.name
455 cfg = fullpath.name
434 if self.profile_dir.copy_config_file(cfg, path=path, overwrite=False):
456 if self.profile_dir.copy_config_file(cfg, path=path, overwrite=False):
435 # file was copied
457 # file was copied
436 self.log.warning("Staging bundled %s from %s into %r"%(
458 self.log.warning("Staging bundled %s from %s into %r"%(
437 cfg, self.profile, self.profile_dir.location)
459 cfg, self.profile, self.profile_dir.location)
438 )
460 )
439
461
440
462
441 def stage_default_config_file(self):
463 def stage_default_config_file(self):
442 """auto generate default config file, and stage it into the profile."""
464 """auto generate default config file, and stage it into the profile."""
443 s = self.generate_config_file()
465 s = self.generate_config_file()
444 config_file = Path(self.profile_dir.location) / self.config_file_name
466 config_file = Path(self.profile_dir.location) / self.config_file_name
445 if self.overwrite or not config_file.exists():
467 if self.overwrite or not config_file.exists():
446 self.log.warning("Generating default config file: %r" % (config_file))
468 self.log.warning("Generating default config file: %r" % (config_file))
447 config_file.write_text(s)
469 config_file.write_text(s)
448
470
449 @catch_config_error
471 @catch_config_error
450 def initialize(self, argv=None):
472 def initialize(self, argv=None):
451 # don't hook up crash handler before parsing command-line
473 # don't hook up crash handler before parsing command-line
452 self.parse_command_line(argv)
474 self.parse_command_line(argv)
453 self.init_crash_handler()
475 self.init_crash_handler()
454 if self.subapp is not None:
476 if self.subapp is not None:
455 # stop here if subapp is taking over
477 # stop here if subapp is taking over
456 return
478 return
457 # save a copy of CLI config to re-load after config files
479 # save a copy of CLI config to re-load after config files
458 # so that it has highest priority
480 # so that it has highest priority
459 cl_config = deepcopy(self.config)
481 cl_config = deepcopy(self.config)
460 self.init_profile_dir()
482 self.init_profile_dir()
461 self.init_config_files()
483 self.init_config_files()
462 self.load_config_file()
484 self.load_config_file()
463 # enforce cl-opts override configfile opts:
485 # enforce cl-opts override configfile opts:
464 self.update_config(cl_config)
486 self.update_config(cl_config)
@@ -1,70 +1,70 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """
2 """
3 Autocall capabilities for IPython.core.
3 Autocall capabilities for IPython.core.
4
4
5 Authors:
5 Authors:
6
6
7 * Brian Granger
7 * Brian Granger
8 * Fernando Perez
8 * Fernando Perez
9 * Thomas Kluyver
9 * Thomas Kluyver
10
10
11 Notes
11 Notes
12 -----
12 -----
13 """
13 """
14
14
15 #-----------------------------------------------------------------------------
15 #-----------------------------------------------------------------------------
16 # Copyright (C) 2008-2011 The IPython Development Team
16 # Copyright (C) 2008-2011 The IPython Development Team
17 #
17 #
18 # Distributed under the terms of the BSD License. The full license is in
18 # Distributed under the terms of the BSD License. The full license is in
19 # the file COPYING, distributed as part of this software.
19 # the file COPYING, distributed as part of this software.
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23 # Imports
23 # Imports
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25
25
26
26
27 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
28 # Code
28 # Code
29 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
30
30
31 class IPyAutocall(object):
31 class IPyAutocall(object):
32 """ Instances of this class are always autocalled
32 """ Instances of this class are always autocalled
33
33
34 This happens regardless of 'autocall' variable state. Use this to
34 This happens regardless of 'autocall' variable state. Use this to
35 develop macro-like mechanisms.
35 develop macro-like mechanisms.
36 """
36 """
37 _ip = None
37 _ip = None
38 rewrite = True
38 rewrite = True
39 def __init__(self, ip=None):
39 def __init__(self, ip=None):
40 self._ip = ip
40 self._ip = ip
41
41
42 def set_ip(self, ip):
42 def set_ip(self, ip):
43 """ Will be used to set _ip point to current ipython instance b/f call
43 """ Will be used to set _ip point to current ipython instance b/f call
44
44
45 Override this method if you don't want this to happen.
45 Override this method if you don't want this to happen.
46
46
47 """
47 """
48 self._ip = ip
48 self._ip = ip
49
49
50
50
51 class ExitAutocall(IPyAutocall):
51 class ExitAutocall(IPyAutocall):
52 """An autocallable object which will be added to the user namespace so that
52 """An autocallable object which will be added to the user namespace so that
53 exit, exit(), quit or quit() are all valid ways to close the shell."""
53 exit, exit(), quit or quit() are all valid ways to close the shell."""
54 rewrite = False
54 rewrite = False
55
55
56 def __call__(self):
56 def __call__(self):
57 self._ip.ask_exit()
57 self._ip.ask_exit()
58
58
59 class ZMQExitAutocall(ExitAutocall):
59 class ZMQExitAutocall(ExitAutocall):
60 """Exit IPython. Autocallable, so it needn't be explicitly called.
60 """Exit IPython. Autocallable, so it needn't be explicitly called.
61
61
62 Parameters
62 Parameters
63 ----------
63 ----------
64 keep_kernel : bool
64 keep_kernel : bool
65 If True, leave the kernel alive. Otherwise, tell the kernel to exit too
65 If True, leave the kernel alive. Otherwise, tell the kernel to exit too
66 (default).
66 (default).
67 """
67 """
68 def __call__(self, keep_kernel=False):
68 def __call__(self, keep_kernel=False):
69 self._ip.keepkernel_on_exit = keep_kernel
69 self._ip.keepkernel_on_exit = keep_kernel
70 self._ip.ask_exit()
70 self._ip.ask_exit()
@@ -1,188 +1,196 b''
1 """Compiler tools with improved interactive support.
1 """Compiler tools with improved interactive support.
2
2
3 Provides compilation machinery similar to codeop, but with caching support so
3 Provides compilation machinery similar to codeop, but with caching support so
4 we can provide interactive tracebacks.
4 we can provide interactive tracebacks.
5
5
6 Authors
6 Authors
7 -------
7 -------
8 * Robert Kern
8 * Robert Kern
9 * Fernando Perez
9 * Fernando Perez
10 * Thomas Kluyver
10 * Thomas Kluyver
11 """
11 """
12
12
13 # Note: though it might be more natural to name this module 'compiler', that
13 # Note: though it might be more natural to name this module 'compiler', that
14 # name is in the stdlib and name collisions with the stdlib tend to produce
14 # name is in the stdlib and name collisions with the stdlib tend to produce
15 # weird problems (often with third-party tools).
15 # weird problems (often with third-party tools).
16
16
17 #-----------------------------------------------------------------------------
17 #-----------------------------------------------------------------------------
18 # Copyright (C) 2010-2011 The IPython Development Team.
18 # Copyright (C) 2010-2011 The IPython Development Team.
19 #
19 #
20 # Distributed under the terms of the BSD License.
20 # Distributed under the terms of the BSD License.
21 #
21 #
22 # The full license is in the file COPYING.txt, distributed with this software.
22 # The full license is in the file COPYING.txt, distributed with this software.
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24
24
25 #-----------------------------------------------------------------------------
25 #-----------------------------------------------------------------------------
26 # Imports
26 # Imports
27 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
28
28
29 # Stdlib imports
29 # Stdlib imports
30 import __future__
30 import __future__
31 from ast import PyCF_ONLY_AST
31 from ast import PyCF_ONLY_AST
32 import codeop
32 import codeop
33 import functools
33 import functools
34 import hashlib
34 import hashlib
35 import linecache
35 import linecache
36 import operator
36 import operator
37 import time
37 import time
38 from contextlib import contextmanager
38 from contextlib import contextmanager
39
39
40 #-----------------------------------------------------------------------------
40 #-----------------------------------------------------------------------------
41 # Constants
41 # Constants
42 #-----------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
43
43
44 # Roughly equal to PyCF_MASK | PyCF_MASK_OBSOLETE as defined in pythonrun.h,
44 # Roughly equal to PyCF_MASK | PyCF_MASK_OBSOLETE as defined in pythonrun.h,
45 # this is used as a bitmask to extract future-related code flags.
45 # this is used as a bitmask to extract future-related code flags.
46 PyCF_MASK = functools.reduce(operator.or_,
46 PyCF_MASK = functools.reduce(operator.or_,
47 (getattr(__future__, fname).compiler_flag
47 (getattr(__future__, fname).compiler_flag
48 for fname in __future__.all_feature_names))
48 for fname in __future__.all_feature_names))
49
49
50 #-----------------------------------------------------------------------------
50 #-----------------------------------------------------------------------------
51 # Local utilities
51 # Local utilities
52 #-----------------------------------------------------------------------------
52 #-----------------------------------------------------------------------------
53
53
54 def code_name(code, number=0):
54 def code_name(code, number=0):
55 """ Compute a (probably) unique name for code for caching.
55 """ Compute a (probably) unique name for code for caching.
56
56
57 This now expects code to be unicode.
57 This now expects code to be unicode.
58 """
58 """
59 hash_digest = hashlib.sha1(code.encode("utf-8")).hexdigest()
59 hash_digest = hashlib.sha1(code.encode("utf-8")).hexdigest()
60 # Include the number and 12 characters of the hash in the name. It's
60 # Include the number and 12 characters of the hash in the name. It's
61 # pretty much impossible that in a single session we'll have collisions
61 # pretty much impossible that in a single session we'll have collisions
62 # even with truncated hashes, and the full one makes tracebacks too long
62 # even with truncated hashes, and the full one makes tracebacks too long
63 return '<ipython-input-{0}-{1}>'.format(number, hash_digest[:12])
63 return '<ipython-input-{0}-{1}>'.format(number, hash_digest[:12])
64
64
65 #-----------------------------------------------------------------------------
65 #-----------------------------------------------------------------------------
66 # Classes and functions
66 # Classes and functions
67 #-----------------------------------------------------------------------------
67 #-----------------------------------------------------------------------------
68
68
69 class CachingCompiler(codeop.Compile):
69 class CachingCompiler(codeop.Compile):
70 """A compiler that caches code compiled from interactive statements.
70 """A compiler that caches code compiled from interactive statements.
71 """
71 """
72
72
73 def __init__(self):
73 def __init__(self):
74 codeop.Compile.__init__(self)
74 codeop.Compile.__init__(self)
75
75
76 # This is ugly, but it must be done this way to allow multiple
76 # This is ugly, but it must be done this way to allow multiple
77 # simultaneous ipython instances to coexist. Since Python itself
77 # simultaneous ipython instances to coexist. Since Python itself
78 # directly accesses the data structures in the linecache module, and
78 # directly accesses the data structures in the linecache module, and
79 # the cache therein is global, we must work with that data structure.
79 # the cache therein is global, we must work with that data structure.
80 # We must hold a reference to the original checkcache routine and call
80 # We must hold a reference to the original checkcache routine and call
81 # that in our own check_cache() below, but the special IPython cache
81 # that in our own check_cache() below, but the special IPython cache
82 # must also be shared by all IPython instances. If we were to hold
82 # must also be shared by all IPython instances. If we were to hold
83 # separate caches (one in each CachingCompiler instance), any call made
83 # separate caches (one in each CachingCompiler instance), any call made
84 # by Python itself to linecache.checkcache() would obliterate the
84 # by Python itself to linecache.checkcache() would obliterate the
85 # cached data from the other IPython instances.
85 # cached data from the other IPython instances.
86 if not hasattr(linecache, '_ipython_cache'):
86 if not hasattr(linecache, '_ipython_cache'):
87 linecache._ipython_cache = {}
87 linecache._ipython_cache = {}
88 if not hasattr(linecache, '_checkcache_ori'):
88 if not hasattr(linecache, '_checkcache_ori'):
89 linecache._checkcache_ori = linecache.checkcache
89 linecache._checkcache_ori = linecache.checkcache
90 # Now, we must monkeypatch the linecache directly so that parts of the
90 # Now, we must monkeypatch the linecache directly so that parts of the
91 # stdlib that call it outside our control go through our codepath
91 # stdlib that call it outside our control go through our codepath
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.
98
102
99 Arguments are exactly the same as ast.parse (in the standard library),
103 Arguments are exactly the same as ast.parse (in the standard library),
100 and are passed to the built-in compile function."""
104 and are passed to the built-in compile function."""
101 return compile(source, filename, symbol, self.flags | PyCF_ONLY_AST, 1)
105 return compile(source, filename, symbol, self.flags | PyCF_ONLY_AST, 1)
102
106
103 def reset_compiler_flags(self):
107 def reset_compiler_flags(self):
104 """Reset compiler flags to default state."""
108 """Reset compiler flags to default state."""
105 # This value is copied from codeop.Compile.__init__, so if that ever
109 # This value is copied from codeop.Compile.__init__, so if that ever
106 # changes, it will need to be updated.
110 # changes, it will need to be updated.
107 self.flags = codeop.PyCF_DONT_IMPLY_DEDENT
111 self.flags = codeop.PyCF_DONT_IMPLY_DEDENT
108
112
109 @property
113 @property
110 def compiler_flags(self):
114 def compiler_flags(self):
111 """Flags currently active in the compilation process.
115 """Flags currently active in the compilation process.
112 """
116 """
113 return self.flags
117 return self.flags
114
118
115 def get_code_name(self, raw_code, transformed_code, number):
119 def get_code_name(self, raw_code, transformed_code, number):
116 """Compute filename given the code, and the cell number.
120 """Compute filename given the code, and the cell number.
117
121
118 Parameters
122 Parameters
119 ----------
123 ----------
120 raw_code : str
124 raw_code : str
121 The raw cell code.
125 The raw cell code.
122 transformed_code : str
126 transformed_code : str
123 The executable Python source code to cache and compile.
127 The executable Python source code to cache and compile.
124 number : int
128 number : int
125 A number which forms part of the code's name. Used for the execution
129 A number which forms part of the code's name. Used for the execution
126 counter.
130 counter.
127
131
128 Returns
132 Returns
129 -------
133 -------
130 The computed filename.
134 The computed filename.
131 """
135 """
132 return code_name(transformed_code, number)
136 return code_name(transformed_code, number)
133
137
134 def cache(self, transformed_code, number=0, raw_code=None):
138 def cache(self, transformed_code, number=0, raw_code=None):
135 """Make a name for a block of code, and cache the code.
139 """Make a name for a block of code, and cache the code.
136
140
137 Parameters
141 Parameters
138 ----------
142 ----------
139 transformed_code : str
143 transformed_code : str
140 The executable Python source code to cache and compile.
144 The executable Python source code to cache and compile.
141 number : int
145 number : int
142 A number which forms part of the code's name. Used for the execution
146 A number which forms part of the code's name. Used for the execution
143 counter.
147 counter.
144 raw_code : str
148 raw_code : str
145 The raw code before transformation, if None, set to `transformed_code`.
149 The raw code before transformation, if None, set to `transformed_code`.
146
150
147 Returns
151 Returns
148 -------
152 -------
149 The name of the cached code (as a string). Pass this as the filename
153 The name of the cached code (as a string). Pass this as the filename
150 argument to compilation, so that tracebacks are correctly hooked up.
154 argument to compilation, so that tracebacks are correctly hooked up.
151 """
155 """
152 if raw_code is None:
156 if raw_code is None:
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(),
159 [line + "\n" for line in transformed_code.splitlines()],
167 [line + "\n" for line in transformed_code.splitlines()],
160 name,
168 name,
161 )
169 )
162 linecache.cache[name] = entry
170 linecache.cache[name] = entry
163 linecache._ipython_cache[name] = entry
171 linecache._ipython_cache[name] = entry
164 return name
172 return name
165
173
166 @contextmanager
174 @contextmanager
167 def extra_flags(self, flags):
175 def extra_flags(self, flags):
168 ## bits that we'll set to 1
176 ## bits that we'll set to 1
169 turn_on_bits = ~self.flags & flags
177 turn_on_bits = ~self.flags & flags
170
178
171
179
172 self.flags = self.flags | flags
180 self.flags = self.flags | flags
173 try:
181 try:
174 yield
182 yield
175 finally:
183 finally:
176 # turn off only the bits we turned on so that something like
184 # turn off only the bits we turned on so that something like
177 # __future__ that set flags stays.
185 # __future__ that set flags stays.
178 self.flags &= ~turn_on_bits
186 self.flags &= ~turn_on_bits
179
187
180
188
181 def check_linecache_ipython(*args):
189 def check_linecache_ipython(*args):
182 """Call linecache.checkcache() safely protecting our cached values.
190 """Call linecache.checkcache() safely protecting our cached values.
183 """
191 """
184 # First call the original checkcache as intended
192 # First call the original checkcache as intended
185 linecache._checkcache_ori(*args)
193 linecache._checkcache_ori(*args)
186 # Then, update back the cache with our data, so that tracebacks related
194 # Then, update back the cache with our data, so that tracebacks related
187 # to our compiled codes can be produced.
195 # to our compiled codes can be produced.
188 linecache.cache.update(linecache._ipython_cache)
196 linecache.cache.update(linecache._ipython_cache)
@@ -1,2239 +1,2239 b''
1 """Completion for IPython.
1 """Completion for IPython.
2
2
3 This module started as fork of the rlcompleter module in the Python standard
3 This module started as fork of the rlcompleter module in the Python standard
4 library. The original enhancements made to rlcompleter have been sent
4 library. The original enhancements made to rlcompleter have been sent
5 upstream and were accepted as of Python 2.3,
5 upstream and were accepted as of Python 2.3,
6
6
7 This module now support a wide variety of completion mechanism both available
7 This module now support a wide variety of completion mechanism both available
8 for normal classic Python code, as well as completer for IPython specific
8 for normal classic Python code, as well as completer for IPython specific
9 Syntax like magics.
9 Syntax like magics.
10
10
11 Latex and Unicode completion
11 Latex and Unicode completion
12 ============================
12 ============================
13
13
14 IPython and compatible frontends not only can complete your code, but can help
14 IPython and compatible frontends not only can complete your code, but can help
15 you to input a wide range of characters. In particular we allow you to insert
15 you to input a wide range of characters. In particular we allow you to insert
16 a unicode character using the tab completion mechanism.
16 a unicode character using the tab completion mechanism.
17
17
18 Forward latex/unicode completion
18 Forward latex/unicode completion
19 --------------------------------
19 --------------------------------
20
20
21 Forward completion allows you to easily type a unicode character using its latex
21 Forward completion allows you to easily type a unicode character using its latex
22 name, or unicode long description. To do so type a backslash follow by the
22 name, or unicode long description. To do so type a backslash follow by the
23 relevant name and press tab:
23 relevant name and press tab:
24
24
25
25
26 Using latex completion:
26 Using latex completion:
27
27
28 .. code::
28 .. code::
29
29
30 \\alpha<tab>
30 \\alpha<tab>
31 α
31 α
32
32
33 or using unicode completion:
33 or using unicode completion:
34
34
35
35
36 .. code::
36 .. code::
37
37
38 \\GREEK SMALL LETTER ALPHA<tab>
38 \\GREEK SMALL LETTER ALPHA<tab>
39 α
39 α
40
40
41
41
42 Only valid Python identifiers will complete. Combining characters (like arrow or
42 Only valid Python identifiers will complete. Combining characters (like arrow or
43 dots) are also available, unlike latex they need to be put after the their
43 dots) are also available, unlike latex they need to be put after the their
44 counterpart that is to say, `F\\\\vec<tab>` is correct, not `\\\\vec<tab>F`.
44 counterpart that is to say, `F\\\\vec<tab>` is correct, not `\\\\vec<tab>F`.
45
45
46 Some browsers are known to display combining characters incorrectly.
46 Some browsers are known to display combining characters incorrectly.
47
47
48 Backward latex completion
48 Backward latex completion
49 -------------------------
49 -------------------------
50
50
51 It is sometime challenging to know how to type a character, if you are using
51 It is sometime challenging to know how to type a character, if you are using
52 IPython, or any compatible frontend you can prepend backslash to the character
52 IPython, or any compatible frontend you can prepend backslash to the character
53 and press `<tab>` to expand it to its latex form.
53 and press `<tab>` to expand it to its latex form.
54
54
55 .. code::
55 .. code::
56
56
57 \\α<tab>
57 \\α<tab>
58 \\alpha
58 \\alpha
59
59
60
60
61 Both forward and backward completions can be deactivated by setting the
61 Both forward and backward completions can be deactivated by setting the
62 ``Completer.backslash_combining_completions`` option to ``False``.
62 ``Completer.backslash_combining_completions`` option to ``False``.
63
63
64
64
65 Experimental
65 Experimental
66 ============
66 ============
67
67
68 Starting with IPython 6.0, this module can make use of the Jedi library to
68 Starting with IPython 6.0, this module can make use of the Jedi library to
69 generate completions both using static analysis of the code, and dynamically
69 generate completions both using static analysis of the code, and dynamically
70 inspecting multiple namespaces. Jedi is an autocompletion and static analysis
70 inspecting multiple namespaces. Jedi is an autocompletion and static analysis
71 for Python. The APIs attached to this new mechanism is unstable and will
71 for Python. The APIs attached to this new mechanism is unstable and will
72 raise unless use in an :any:`provisionalcompleter` context manager.
72 raise unless use in an :any:`provisionalcompleter` context manager.
73
73
74 You will find that the following are experimental:
74 You will find that the following are experimental:
75
75
76 - :any:`provisionalcompleter`
76 - :any:`provisionalcompleter`
77 - :any:`IPCompleter.completions`
77 - :any:`IPCompleter.completions`
78 - :any:`Completion`
78 - :any:`Completion`
79 - :any:`rectify_completions`
79 - :any:`rectify_completions`
80
80
81 .. note::
81 .. note::
82
82
83 better name for :any:`rectify_completions` ?
83 better name for :any:`rectify_completions` ?
84
84
85 We welcome any feedback on these new API, and we also encourage you to try this
85 We welcome any feedback on these new API, and we also encourage you to try this
86 module in debug mode (start IPython with ``--Completer.debug=True``) in order
86 module in debug mode (start IPython with ``--Completer.debug=True``) in order
87 to have extra logging information if :any:`jedi` is crashing, or if current
87 to have extra logging information if :any:`jedi` is crashing, or if current
88 IPython completer pending deprecations are returning results not yet handled
88 IPython completer pending deprecations are returning results not yet handled
89 by :any:`jedi`
89 by :any:`jedi`
90
90
91 Using Jedi for tab completion allow snippets like the following to work without
91 Using Jedi for tab completion allow snippets like the following to work without
92 having to execute any code:
92 having to execute any code:
93
93
94 >>> myvar = ['hello', 42]
94 >>> myvar = ['hello', 42]
95 ... myvar[1].bi<tab>
95 ... myvar[1].bi<tab>
96
96
97 Tab completion will be able to infer that ``myvar[1]`` is a real number without
97 Tab completion will be able to infer that ``myvar[1]`` is a real number without
98 executing any code unlike the previously available ``IPCompleter.greedy``
98 executing any code unlike the previously available ``IPCompleter.greedy``
99 option.
99 option.
100
100
101 Be sure to update :any:`jedi` to the latest stable version or to try the
101 Be sure to update :any:`jedi` to the latest stable version or to try the
102 current development version to get better completions.
102 current development version to get better completions.
103 """
103 """
104
104
105
105
106 # Copyright (c) IPython Development Team.
106 # Copyright (c) IPython Development Team.
107 # Distributed under the terms of the Modified BSD License.
107 # Distributed under the terms of the Modified BSD License.
108 #
108 #
109 # Some of this code originated from rlcompleter in the Python standard library
109 # Some of this code originated from rlcompleter in the Python standard library
110 # Copyright (C) 2001 Python Software Foundation, www.python.org
110 # Copyright (C) 2001 Python Software Foundation, www.python.org
111
111
112
112
113 import builtins as builtin_mod
113 import builtins as builtin_mod
114 import glob
114 import glob
115 import inspect
115 import inspect
116 import itertools
116 import itertools
117 import keyword
117 import keyword
118 import os
118 import os
119 import re
119 import re
120 import string
120 import string
121 import sys
121 import sys
122 import time
122 import time
123 import unicodedata
123 import unicodedata
124 import uuid
124 import uuid
125 import warnings
125 import warnings
126 from contextlib import contextmanager
126 from contextlib import contextmanager
127 from importlib import import_module
127 from importlib import import_module
128 from types import SimpleNamespace
128 from types import SimpleNamespace
129 from typing import Iterable, Iterator, List, Tuple, Union, Any, Sequence, Dict, NamedTuple, Pattern, Optional
129 from typing import Iterable, Iterator, List, Tuple, Union, Any, Sequence, Dict, NamedTuple, Pattern, Optional
130
130
131 from IPython.core.error import TryNext
131 from IPython.core.error import TryNext
132 from IPython.core.inputtransformer2 import ESC_MAGIC
132 from IPython.core.inputtransformer2 import ESC_MAGIC
133 from IPython.core.latex_symbols import latex_symbols, reverse_latex_symbol
133 from IPython.core.latex_symbols import latex_symbols, reverse_latex_symbol
134 from IPython.core.oinspect import InspectColors
134 from IPython.core.oinspect import InspectColors
135 from IPython.utils import generics
135 from IPython.utils import generics
136 from IPython.utils.dir2 import dir2, get_real_method
136 from IPython.utils.dir2 import dir2, get_real_method
137 from IPython.utils.path import ensure_dir_exists
137 from IPython.utils.path import ensure_dir_exists
138 from IPython.utils.process import arg_split
138 from IPython.utils.process import arg_split
139 from traitlets import Bool, Enum, Int, List as ListTrait, Unicode, default, observe
139 from traitlets import Bool, Enum, Int, List as ListTrait, Unicode, default, observe
140 from traitlets.config.configurable import Configurable
140 from traitlets.config.configurable import Configurable
141
141
142 import __main__
142 import __main__
143
143
144 # skip module docstests
144 # skip module docstests
145 skip_doctest = True
145 skip_doctest = True
146
146
147 try:
147 try:
148 import jedi
148 import jedi
149 jedi.settings.case_insensitive_completion = False
149 jedi.settings.case_insensitive_completion = False
150 import jedi.api.helpers
150 import jedi.api.helpers
151 import jedi.api.classes
151 import jedi.api.classes
152 JEDI_INSTALLED = True
152 JEDI_INSTALLED = True
153 except ImportError:
153 except ImportError:
154 JEDI_INSTALLED = False
154 JEDI_INSTALLED = False
155 #-----------------------------------------------------------------------------
155 #-----------------------------------------------------------------------------
156 # Globals
156 # Globals
157 #-----------------------------------------------------------------------------
157 #-----------------------------------------------------------------------------
158
158
159 # ranges where we have most of the valid unicode names. We could be more finer
159 # ranges where we have most of the valid unicode names. We could be more finer
160 # grained but is it worth it for performace While unicode have character in the
160 # grained but is it worth it for performace While unicode have character in the
161 # rage 0, 0x110000, we seem to have name for about 10% of those. (131808 as I
161 # rage 0, 0x110000, we seem to have name for about 10% of those. (131808 as I
162 # write this). With below range we cover them all, with a density of ~67%
162 # write this). With below range we cover them all, with a density of ~67%
163 # biggest next gap we consider only adds up about 1% density and there are 600
163 # biggest next gap we consider only adds up about 1% density and there are 600
164 # gaps that would need hard coding.
164 # gaps that would need hard coding.
165 _UNICODE_RANGES = [(32, 0x3134b), (0xe0001, 0xe01f0)]
165 _UNICODE_RANGES = [(32, 0x3134b), (0xe0001, 0xe01f0)]
166
166
167 # Public API
167 # Public API
168 __all__ = ['Completer','IPCompleter']
168 __all__ = ['Completer','IPCompleter']
169
169
170 if sys.platform == 'win32':
170 if sys.platform == 'win32':
171 PROTECTABLES = ' '
171 PROTECTABLES = ' '
172 else:
172 else:
173 PROTECTABLES = ' ()[]{}?=\\|;:\'#*"^&'
173 PROTECTABLES = ' ()[]{}?=\\|;:\'#*"^&'
174
174
175 # Protect against returning an enormous number of completions which the frontend
175 # Protect against returning an enormous number of completions which the frontend
176 # may have trouble processing.
176 # may have trouble processing.
177 MATCHES_LIMIT = 500
177 MATCHES_LIMIT = 500
178
178
179 _deprecation_readline_sentinel = object()
179 _deprecation_readline_sentinel = object()
180
180
181
181
182 class ProvisionalCompleterWarning(FutureWarning):
182 class ProvisionalCompleterWarning(FutureWarning):
183 """
183 """
184 Exception raise by an experimental feature in this module.
184 Exception raise by an experimental feature in this module.
185
185
186 Wrap code in :any:`provisionalcompleter` context manager if you
186 Wrap code in :any:`provisionalcompleter` context manager if you
187 are certain you want to use an unstable feature.
187 are certain you want to use an unstable feature.
188 """
188 """
189 pass
189 pass
190
190
191 warnings.filterwarnings('error', category=ProvisionalCompleterWarning)
191 warnings.filterwarnings('error', category=ProvisionalCompleterWarning)
192
192
193 @contextmanager
193 @contextmanager
194 def provisionalcompleter(action='ignore'):
194 def provisionalcompleter(action='ignore'):
195 """
195 """
196 This context manager has to be used in any place where unstable completer
196 This context manager has to be used in any place where unstable completer
197 behavior and API may be called.
197 behavior and API may be called.
198
198
199 >>> with provisionalcompleter():
199 >>> with provisionalcompleter():
200 ... completer.do_experimental_things() # works
200 ... completer.do_experimental_things() # works
201
201
202 >>> completer.do_experimental_things() # raises.
202 >>> completer.do_experimental_things() # raises.
203
203
204 .. note::
204 .. note::
205
205
206 Unstable
206 Unstable
207
207
208 By using this context manager you agree that the API in use may change
208 By using this context manager you agree that the API in use may change
209 without warning, and that you won't complain if they do so.
209 without warning, and that you won't complain if they do so.
210
210
211 You also understand that, if the API is not to your liking, you should report
211 You also understand that, if the API is not to your liking, you should report
212 a bug to explain your use case upstream.
212 a bug to explain your use case upstream.
213
213
214 We'll be happy to get your feedback, feature requests, and improvements on
214 We'll be happy to get your feedback, feature requests, and improvements on
215 any of the unstable APIs!
215 any of the unstable APIs!
216 """
216 """
217 with warnings.catch_warnings():
217 with warnings.catch_warnings():
218 warnings.filterwarnings(action, category=ProvisionalCompleterWarning)
218 warnings.filterwarnings(action, category=ProvisionalCompleterWarning)
219 yield
219 yield
220
220
221
221
222 def has_open_quotes(s):
222 def has_open_quotes(s):
223 """Return whether a string has open quotes.
223 """Return whether a string has open quotes.
224
224
225 This simply counts whether the number of quote characters of either type in
225 This simply counts whether the number of quote characters of either type in
226 the string is odd.
226 the string is odd.
227
227
228 Returns
228 Returns
229 -------
229 -------
230 If there is an open quote, the quote character is returned. Else, return
230 If there is an open quote, the quote character is returned. Else, return
231 False.
231 False.
232 """
232 """
233 # We check " first, then ', so complex cases with nested quotes will get
233 # We check " first, then ', so complex cases with nested quotes will get
234 # the " to take precedence.
234 # the " to take precedence.
235 if s.count('"') % 2:
235 if s.count('"') % 2:
236 return '"'
236 return '"'
237 elif s.count("'") % 2:
237 elif s.count("'") % 2:
238 return "'"
238 return "'"
239 else:
239 else:
240 return False
240 return False
241
241
242
242
243 def protect_filename(s, protectables=PROTECTABLES):
243 def protect_filename(s, protectables=PROTECTABLES):
244 """Escape a string to protect certain characters."""
244 """Escape a string to protect certain characters."""
245 if set(s) & set(protectables):
245 if set(s) & set(protectables):
246 if sys.platform == "win32":
246 if sys.platform == "win32":
247 return '"' + s + '"'
247 return '"' + s + '"'
248 else:
248 else:
249 return "".join(("\\" + c if c in protectables else c) for c in s)
249 return "".join(("\\" + c if c in protectables else c) for c in s)
250 else:
250 else:
251 return s
251 return s
252
252
253
253
254 def expand_user(path:str) -> Tuple[str, bool, str]:
254 def expand_user(path:str) -> Tuple[str, bool, str]:
255 """Expand ``~``-style usernames in strings.
255 """Expand ``~``-style usernames in strings.
256
256
257 This is similar to :func:`os.path.expanduser`, but it computes and returns
257 This is similar to :func:`os.path.expanduser`, but it computes and returns
258 extra information that will be useful if the input was being used in
258 extra information that will be useful if the input was being used in
259 computing completions, and you wish to return the completions with the
259 computing completions, and you wish to return the completions with the
260 original '~' instead of its expanded value.
260 original '~' instead of its expanded value.
261
261
262 Parameters
262 Parameters
263 ----------
263 ----------
264 path : str
264 path : str
265 String to be expanded. If no ~ is present, the output is the same as the
265 String to be expanded. If no ~ is present, the output is the same as the
266 input.
266 input.
267
267
268 Returns
268 Returns
269 -------
269 -------
270 newpath : str
270 newpath : str
271 Result of ~ expansion in the input path.
271 Result of ~ expansion in the input path.
272 tilde_expand : bool
272 tilde_expand : bool
273 Whether any expansion was performed or not.
273 Whether any expansion was performed or not.
274 tilde_val : str
274 tilde_val : str
275 The value that ~ was replaced with.
275 The value that ~ was replaced with.
276 """
276 """
277 # Default values
277 # Default values
278 tilde_expand = False
278 tilde_expand = False
279 tilde_val = ''
279 tilde_val = ''
280 newpath = path
280 newpath = path
281
281
282 if path.startswith('~'):
282 if path.startswith('~'):
283 tilde_expand = True
283 tilde_expand = True
284 rest = len(path)-1
284 rest = len(path)-1
285 newpath = os.path.expanduser(path)
285 newpath = os.path.expanduser(path)
286 if rest:
286 if rest:
287 tilde_val = newpath[:-rest]
287 tilde_val = newpath[:-rest]
288 else:
288 else:
289 tilde_val = newpath
289 tilde_val = newpath
290
290
291 return newpath, tilde_expand, tilde_val
291 return newpath, tilde_expand, tilde_val
292
292
293
293
294 def compress_user(path:str, tilde_expand:bool, tilde_val:str) -> str:
294 def compress_user(path:str, tilde_expand:bool, tilde_val:str) -> str:
295 """Does the opposite of expand_user, with its outputs.
295 """Does the opposite of expand_user, with its outputs.
296 """
296 """
297 if tilde_expand:
297 if tilde_expand:
298 return path.replace(tilde_val, '~')
298 return path.replace(tilde_val, '~')
299 else:
299 else:
300 return path
300 return path
301
301
302
302
303 def completions_sorting_key(word):
303 def completions_sorting_key(word):
304 """key for sorting completions
304 """key for sorting completions
305
305
306 This does several things:
306 This does several things:
307
307
308 - Demote any completions starting with underscores to the end
308 - Demote any completions starting with underscores to the end
309 - Insert any %magic and %%cellmagic completions in the alphabetical order
309 - Insert any %magic and %%cellmagic completions in the alphabetical order
310 by their name
310 by their name
311 """
311 """
312 prio1, prio2 = 0, 0
312 prio1, prio2 = 0, 0
313
313
314 if word.startswith('__'):
314 if word.startswith('__'):
315 prio1 = 2
315 prio1 = 2
316 elif word.startswith('_'):
316 elif word.startswith('_'):
317 prio1 = 1
317 prio1 = 1
318
318
319 if word.endswith('='):
319 if word.endswith('='):
320 prio1 = -1
320 prio1 = -1
321
321
322 if word.startswith('%%'):
322 if word.startswith('%%'):
323 # If there's another % in there, this is something else, so leave it alone
323 # If there's another % in there, this is something else, so leave it alone
324 if not "%" in word[2:]:
324 if not "%" in word[2:]:
325 word = word[2:]
325 word = word[2:]
326 prio2 = 2
326 prio2 = 2
327 elif word.startswith('%'):
327 elif word.startswith('%'):
328 if not "%" in word[1:]:
328 if not "%" in word[1:]:
329 word = word[1:]
329 word = word[1:]
330 prio2 = 1
330 prio2 = 1
331
331
332 return prio1, word, prio2
332 return prio1, word, prio2
333
333
334
334
335 class _FakeJediCompletion:
335 class _FakeJediCompletion:
336 """
336 """
337 This is a workaround to communicate to the UI that Jedi has crashed and to
337 This is a workaround to communicate to the UI that Jedi has crashed and to
338 report a bug. Will be used only id :any:`IPCompleter.debug` is set to true.
338 report a bug. Will be used only id :any:`IPCompleter.debug` is set to true.
339
339
340 Added in IPython 6.0 so should likely be removed for 7.0
340 Added in IPython 6.0 so should likely be removed for 7.0
341
341
342 """
342 """
343
343
344 def __init__(self, name):
344 def __init__(self, name):
345
345
346 self.name = name
346 self.name = name
347 self.complete = name
347 self.complete = name
348 self.type = 'crashed'
348 self.type = 'crashed'
349 self.name_with_symbols = name
349 self.name_with_symbols = name
350 self.signature = ''
350 self.signature = ''
351 self._origin = 'fake'
351 self._origin = 'fake'
352
352
353 def __repr__(self):
353 def __repr__(self):
354 return '<Fake completion object jedi has crashed>'
354 return '<Fake completion object jedi has crashed>'
355
355
356
356
357 class Completion:
357 class Completion:
358 """
358 """
359 Completion object used and return by IPython completers.
359 Completion object used and return by IPython completers.
360
360
361 .. warning::
361 .. warning::
362
362
363 Unstable
363 Unstable
364
364
365 This function is unstable, API may change without warning.
365 This function is unstable, API may change without warning.
366 It will also raise unless use in proper context manager.
366 It will also raise unless use in proper context manager.
367
367
368 This act as a middle ground :any:`Completion` object between the
368 This act as a middle ground :any:`Completion` object between the
369 :any:`jedi.api.classes.Completion` object and the Prompt Toolkit completion
369 :any:`jedi.api.classes.Completion` object and the Prompt Toolkit completion
370 object. While Jedi need a lot of information about evaluator and how the
370 object. While Jedi need a lot of information about evaluator and how the
371 code should be ran/inspected, PromptToolkit (and other frontend) mostly
371 code should be ran/inspected, PromptToolkit (and other frontend) mostly
372 need user facing information.
372 need user facing information.
373
373
374 - Which range should be replaced replaced by what.
374 - Which range should be replaced replaced by what.
375 - Some metadata (like completion type), or meta information to displayed to
375 - Some metadata (like completion type), or meta information to displayed to
376 the use user.
376 the use user.
377
377
378 For debugging purpose we can also store the origin of the completion (``jedi``,
378 For debugging purpose we can also store the origin of the completion (``jedi``,
379 ``IPython.python_matches``, ``IPython.magics_matches``...).
379 ``IPython.python_matches``, ``IPython.magics_matches``...).
380 """
380 """
381
381
382 __slots__ = ['start', 'end', 'text', 'type', 'signature', '_origin']
382 __slots__ = ['start', 'end', 'text', 'type', 'signature', '_origin']
383
383
384 def __init__(self, start: int, end: int, text: str, *, type: str=None, _origin='', signature='') -> None:
384 def __init__(self, start: int, end: int, text: str, *, type: str=None, _origin='', signature='') -> None:
385 warnings.warn("``Completion`` is a provisional API (as of IPython 6.0). "
385 warnings.warn("``Completion`` is a provisional API (as of IPython 6.0). "
386 "It may change without warnings. "
386 "It may change without warnings. "
387 "Use in corresponding context manager.",
387 "Use in corresponding context manager.",
388 category=ProvisionalCompleterWarning, stacklevel=2)
388 category=ProvisionalCompleterWarning, stacklevel=2)
389
389
390 self.start = start
390 self.start = start
391 self.end = end
391 self.end = end
392 self.text = text
392 self.text = text
393 self.type = type
393 self.type = type
394 self.signature = signature
394 self.signature = signature
395 self._origin = _origin
395 self._origin = _origin
396
396
397 def __repr__(self):
397 def __repr__(self):
398 return '<Completion start=%s end=%s text=%r type=%r, signature=%r,>' % \
398 return '<Completion start=%s end=%s text=%r type=%r, signature=%r,>' % \
399 (self.start, self.end, self.text, self.type or '?', self.signature or '?')
399 (self.start, self.end, self.text, self.type or '?', self.signature or '?')
400
400
401 def __eq__(self, other)->Bool:
401 def __eq__(self, other)->Bool:
402 """
402 """
403 Equality and hash do not hash the type (as some completer may not be
403 Equality and hash do not hash the type (as some completer may not be
404 able to infer the type), but are use to (partially) de-duplicate
404 able to infer the type), but are use to (partially) de-duplicate
405 completion.
405 completion.
406
406
407 Completely de-duplicating completion is a bit tricker that just
407 Completely de-duplicating completion is a bit tricker that just
408 comparing as it depends on surrounding text, which Completions are not
408 comparing as it depends on surrounding text, which Completions are not
409 aware of.
409 aware of.
410 """
410 """
411 return self.start == other.start and \
411 return self.start == other.start and \
412 self.end == other.end and \
412 self.end == other.end and \
413 self.text == other.text
413 self.text == other.text
414
414
415 def __hash__(self):
415 def __hash__(self):
416 return hash((self.start, self.end, self.text))
416 return hash((self.start, self.end, self.text))
417
417
418
418
419 _IC = Iterable[Completion]
419 _IC = Iterable[Completion]
420
420
421
421
422 def _deduplicate_completions(text: str, completions: _IC)-> _IC:
422 def _deduplicate_completions(text: str, completions: _IC)-> _IC:
423 """
423 """
424 Deduplicate a set of completions.
424 Deduplicate a set of completions.
425
425
426 .. warning::
426 .. warning::
427
427
428 Unstable
428 Unstable
429
429
430 This function is unstable, API may change without warning.
430 This function is unstable, API may change without warning.
431
431
432 Parameters
432 Parameters
433 ----------
433 ----------
434 text: str
434 text : str
435 text that should be completed.
435 text that should be completed.
436 completions: Iterator[Completion]
436 completions : Iterator[Completion]
437 iterator over the completions to deduplicate
437 iterator over the completions to deduplicate
438
438
439 Yields
439 Yields
440 ------
440 ------
441 `Completions` objects
441 `Completions` objects
442 Completions coming from multiple sources, may be different but end up having
442 Completions coming from multiple sources, may be different but end up having
443 the same effect when applied to ``text``. If this is the case, this will
443 the same effect when applied to ``text``. If this is the case, this will
444 consider completions as equal and only emit the first encountered.
444 consider completions as equal and only emit the first encountered.
445 Not folded in `completions()` yet for debugging purpose, and to detect when
445 Not folded in `completions()` yet for debugging purpose, and to detect when
446 the IPython completer does return things that Jedi does not, but should be
446 the IPython completer does return things that Jedi does not, but should be
447 at some point.
447 at some point.
448 """
448 """
449 completions = list(completions)
449 completions = list(completions)
450 if not completions:
450 if not completions:
451 return
451 return
452
452
453 new_start = min(c.start for c in completions)
453 new_start = min(c.start for c in completions)
454 new_end = max(c.end for c in completions)
454 new_end = max(c.end for c in completions)
455
455
456 seen = set()
456 seen = set()
457 for c in completions:
457 for c in completions:
458 new_text = text[new_start:c.start] + c.text + text[c.end:new_end]
458 new_text = text[new_start:c.start] + c.text + text[c.end:new_end]
459 if new_text not in seen:
459 if new_text not in seen:
460 yield c
460 yield c
461 seen.add(new_text)
461 seen.add(new_text)
462
462
463
463
464 def rectify_completions(text: str, completions: _IC, *, _debug=False)->_IC:
464 def rectify_completions(text: str, completions: _IC, *, _debug=False)->_IC:
465 """
465 """
466 Rectify a set of completions to all have the same ``start`` and ``end``
466 Rectify a set of completions to all have the same ``start`` and ``end``
467
467
468 .. warning::
468 .. warning::
469
469
470 Unstable
470 Unstable
471
471
472 This function is unstable, API may change without warning.
472 This function is unstable, API may change without warning.
473 It will also raise unless use in proper context manager.
473 It will also raise unless use in proper context manager.
474
474
475 Parameters
475 Parameters
476 ----------
476 ----------
477 text: str
477 text : str
478 text that should be completed.
478 text that should be completed.
479 completions: Iterator[Completion]
479 completions : Iterator[Completion]
480 iterator over the completions to rectify
480 iterator over the completions to rectify
481
481
482 Notes
482 Notes
483 -----
483 -----
484 :any:`jedi.api.classes.Completion` s returned by Jedi may not have the same start and end, though
484 :any:`jedi.api.classes.Completion` s returned by Jedi may not have the same start and end, though
485 the Jupyter Protocol requires them to behave like so. This will readjust
485 the Jupyter Protocol requires them to behave like so. This will readjust
486 the completion to have the same ``start`` and ``end`` by padding both
486 the completion to have the same ``start`` and ``end`` by padding both
487 extremities with surrounding text.
487 extremities with surrounding text.
488
488
489 During stabilisation should support a ``_debug`` option to log which
489 During stabilisation should support a ``_debug`` option to log which
490 completion are return by the IPython completer and not found in Jedi in
490 completion are return by the IPython completer and not found in Jedi in
491 order to make upstream bug report.
491 order to make upstream bug report.
492 """
492 """
493 warnings.warn("`rectify_completions` is a provisional API (as of IPython 6.0). "
493 warnings.warn("`rectify_completions` is a provisional API (as of IPython 6.0). "
494 "It may change without warnings. "
494 "It may change without warnings. "
495 "Use in corresponding context manager.",
495 "Use in corresponding context manager.",
496 category=ProvisionalCompleterWarning, stacklevel=2)
496 category=ProvisionalCompleterWarning, stacklevel=2)
497
497
498 completions = list(completions)
498 completions = list(completions)
499 if not completions:
499 if not completions:
500 return
500 return
501 starts = (c.start for c in completions)
501 starts = (c.start for c in completions)
502 ends = (c.end for c in completions)
502 ends = (c.end for c in completions)
503
503
504 new_start = min(starts)
504 new_start = min(starts)
505 new_end = max(ends)
505 new_end = max(ends)
506
506
507 seen_jedi = set()
507 seen_jedi = set()
508 seen_python_matches = set()
508 seen_python_matches = set()
509 for c in completions:
509 for c in completions:
510 new_text = text[new_start:c.start] + c.text + text[c.end:new_end]
510 new_text = text[new_start:c.start] + c.text + text[c.end:new_end]
511 if c._origin == 'jedi':
511 if c._origin == 'jedi':
512 seen_jedi.add(new_text)
512 seen_jedi.add(new_text)
513 elif c._origin == 'IPCompleter.python_matches':
513 elif c._origin == 'IPCompleter.python_matches':
514 seen_python_matches.add(new_text)
514 seen_python_matches.add(new_text)
515 yield Completion(new_start, new_end, new_text, type=c.type, _origin=c._origin, signature=c.signature)
515 yield Completion(new_start, new_end, new_text, type=c.type, _origin=c._origin, signature=c.signature)
516 diff = seen_python_matches.difference(seen_jedi)
516 diff = seen_python_matches.difference(seen_jedi)
517 if diff and _debug:
517 if diff and _debug:
518 print('IPython.python matches have extras:', diff)
518 print('IPython.python matches have extras:', diff)
519
519
520
520
521 if sys.platform == 'win32':
521 if sys.platform == 'win32':
522 DELIMS = ' \t\n`!@#$^&*()=+[{]}|;\'",<>?'
522 DELIMS = ' \t\n`!@#$^&*()=+[{]}|;\'",<>?'
523 else:
523 else:
524 DELIMS = ' \t\n`!@#$^&*()=+[{]}\\|;:\'",<>?'
524 DELIMS = ' \t\n`!@#$^&*()=+[{]}\\|;:\'",<>?'
525
525
526 GREEDY_DELIMS = ' =\r\n'
526 GREEDY_DELIMS = ' =\r\n'
527
527
528
528
529 class CompletionSplitter(object):
529 class CompletionSplitter(object):
530 """An object to split an input line in a manner similar to readline.
530 """An object to split an input line in a manner similar to readline.
531
531
532 By having our own implementation, we can expose readline-like completion in
532 By having our own implementation, we can expose readline-like completion in
533 a uniform manner to all frontends. This object only needs to be given the
533 a uniform manner to all frontends. This object only needs to be given the
534 line of text to be split and the cursor position on said line, and it
534 line of text to be split and the cursor position on said line, and it
535 returns the 'word' to be completed on at the cursor after splitting the
535 returns the 'word' to be completed on at the cursor after splitting the
536 entire line.
536 entire line.
537
537
538 What characters are used as splitting delimiters can be controlled by
538 What characters are used as splitting delimiters can be controlled by
539 setting the ``delims`` attribute (this is a property that internally
539 setting the ``delims`` attribute (this is a property that internally
540 automatically builds the necessary regular expression)"""
540 automatically builds the necessary regular expression)"""
541
541
542 # Private interface
542 # Private interface
543
543
544 # A string of delimiter characters. The default value makes sense for
544 # A string of delimiter characters. The default value makes sense for
545 # IPython's most typical usage patterns.
545 # IPython's most typical usage patterns.
546 _delims = DELIMS
546 _delims = DELIMS
547
547
548 # The expression (a normal string) to be compiled into a regular expression
548 # The expression (a normal string) to be compiled into a regular expression
549 # for actual splitting. We store it as an attribute mostly for ease of
549 # for actual splitting. We store it as an attribute mostly for ease of
550 # debugging, since this type of code can be so tricky to debug.
550 # debugging, since this type of code can be so tricky to debug.
551 _delim_expr = None
551 _delim_expr = None
552
552
553 # The regular expression that does the actual splitting
553 # The regular expression that does the actual splitting
554 _delim_re = None
554 _delim_re = None
555
555
556 def __init__(self, delims=None):
556 def __init__(self, delims=None):
557 delims = CompletionSplitter._delims if delims is None else delims
557 delims = CompletionSplitter._delims if delims is None else delims
558 self.delims = delims
558 self.delims = delims
559
559
560 @property
560 @property
561 def delims(self):
561 def delims(self):
562 """Return the string of delimiter characters."""
562 """Return the string of delimiter characters."""
563 return self._delims
563 return self._delims
564
564
565 @delims.setter
565 @delims.setter
566 def delims(self, delims):
566 def delims(self, delims):
567 """Set the delimiters for line splitting."""
567 """Set the delimiters for line splitting."""
568 expr = '[' + ''.join('\\'+ c for c in delims) + ']'
568 expr = '[' + ''.join('\\'+ c for c in delims) + ']'
569 self._delim_re = re.compile(expr)
569 self._delim_re = re.compile(expr)
570 self._delims = delims
570 self._delims = delims
571 self._delim_expr = expr
571 self._delim_expr = expr
572
572
573 def split_line(self, line, cursor_pos=None):
573 def split_line(self, line, cursor_pos=None):
574 """Split a line of text with a cursor at the given position.
574 """Split a line of text with a cursor at the given position.
575 """
575 """
576 l = line if cursor_pos is None else line[:cursor_pos]
576 l = line if cursor_pos is None else line[:cursor_pos]
577 return self._delim_re.split(l)[-1]
577 return self._delim_re.split(l)[-1]
578
578
579
579
580
580
581 class Completer(Configurable):
581 class Completer(Configurable):
582
582
583 greedy = Bool(False,
583 greedy = Bool(False,
584 help="""Activate greedy completion
584 help="""Activate greedy completion
585 PENDING DEPRECTION. this is now mostly taken care of with Jedi.
585 PENDING DEPRECTION. this is now mostly taken care of with Jedi.
586
586
587 This will enable completion on elements of lists, results of function calls, etc.,
587 This will enable completion on elements of lists, results of function calls, etc.,
588 but can be unsafe because the code is actually evaluated on TAB.
588 but can be unsafe because the code is actually evaluated on TAB.
589 """
589 """
590 ).tag(config=True)
590 ).tag(config=True)
591
591
592 use_jedi = Bool(default_value=JEDI_INSTALLED,
592 use_jedi = Bool(default_value=JEDI_INSTALLED,
593 help="Experimental: Use Jedi to generate autocompletions. "
593 help="Experimental: Use Jedi to generate autocompletions. "
594 "Default to True if jedi is installed.").tag(config=True)
594 "Default to True if jedi is installed.").tag(config=True)
595
595
596 jedi_compute_type_timeout = Int(default_value=400,
596 jedi_compute_type_timeout = Int(default_value=400,
597 help="""Experimental: restrict time (in milliseconds) during which Jedi can compute types.
597 help="""Experimental: restrict time (in milliseconds) during which Jedi can compute types.
598 Set to 0 to stop computing types. Non-zero value lower than 100ms may hurt
598 Set to 0 to stop computing types. Non-zero value lower than 100ms may hurt
599 performance by preventing jedi to build its cache.
599 performance by preventing jedi to build its cache.
600 """).tag(config=True)
600 """).tag(config=True)
601
601
602 debug = Bool(default_value=False,
602 debug = Bool(default_value=False,
603 help='Enable debug for the Completer. Mostly print extra '
603 help='Enable debug for the Completer. Mostly print extra '
604 'information for experimental jedi integration.')\
604 'information for experimental jedi integration.')\
605 .tag(config=True)
605 .tag(config=True)
606
606
607 backslash_combining_completions = Bool(True,
607 backslash_combining_completions = Bool(True,
608 help="Enable unicode completions, e.g. \\alpha<tab> . "
608 help="Enable unicode completions, e.g. \\alpha<tab> . "
609 "Includes completion of latex commands, unicode names, and expanding "
609 "Includes completion of latex commands, unicode names, and expanding "
610 "unicode characters back to latex commands.").tag(config=True)
610 "unicode characters back to latex commands.").tag(config=True)
611
611
612
612
613
613
614 def __init__(self, namespace=None, global_namespace=None, **kwargs):
614 def __init__(self, namespace=None, global_namespace=None, **kwargs):
615 """Create a new completer for the command line.
615 """Create a new completer for the command line.
616
616
617 Completer(namespace=ns, global_namespace=ns2) -> completer instance.
617 Completer(namespace=ns, global_namespace=ns2) -> completer instance.
618
618
619 If unspecified, the default namespace where completions are performed
619 If unspecified, the default namespace where completions are performed
620 is __main__ (technically, __main__.__dict__). Namespaces should be
620 is __main__ (technically, __main__.__dict__). Namespaces should be
621 given as dictionaries.
621 given as dictionaries.
622
622
623 An optional second namespace can be given. This allows the completer
623 An optional second namespace can be given. This allows the completer
624 to handle cases where both the local and global scopes need to be
624 to handle cases where both the local and global scopes need to be
625 distinguished.
625 distinguished.
626 """
626 """
627
627
628 # Don't bind to namespace quite yet, but flag whether the user wants a
628 # Don't bind to namespace quite yet, but flag whether the user wants a
629 # specific namespace or to use __main__.__dict__. This will allow us
629 # specific namespace or to use __main__.__dict__. This will allow us
630 # to bind to __main__.__dict__ at completion time, not now.
630 # to bind to __main__.__dict__ at completion time, not now.
631 if namespace is None:
631 if namespace is None:
632 self.use_main_ns = True
632 self.use_main_ns = True
633 else:
633 else:
634 self.use_main_ns = False
634 self.use_main_ns = False
635 self.namespace = namespace
635 self.namespace = namespace
636
636
637 # The global namespace, if given, can be bound directly
637 # The global namespace, if given, can be bound directly
638 if global_namespace is None:
638 if global_namespace is None:
639 self.global_namespace = {}
639 self.global_namespace = {}
640 else:
640 else:
641 self.global_namespace = global_namespace
641 self.global_namespace = global_namespace
642
642
643 self.custom_matchers = []
643 self.custom_matchers = []
644
644
645 super(Completer, self).__init__(**kwargs)
645 super(Completer, self).__init__(**kwargs)
646
646
647 def complete(self, text, state):
647 def complete(self, text, state):
648 """Return the next possible completion for 'text'.
648 """Return the next possible completion for 'text'.
649
649
650 This is called successively with state == 0, 1, 2, ... until it
650 This is called successively with state == 0, 1, 2, ... until it
651 returns None. The completion should begin with 'text'.
651 returns None. The completion should begin with 'text'.
652
652
653 """
653 """
654 if self.use_main_ns:
654 if self.use_main_ns:
655 self.namespace = __main__.__dict__
655 self.namespace = __main__.__dict__
656
656
657 if state == 0:
657 if state == 0:
658 if "." in text:
658 if "." in text:
659 self.matches = self.attr_matches(text)
659 self.matches = self.attr_matches(text)
660 else:
660 else:
661 self.matches = self.global_matches(text)
661 self.matches = self.global_matches(text)
662 try:
662 try:
663 return self.matches[state]
663 return self.matches[state]
664 except IndexError:
664 except IndexError:
665 return None
665 return None
666
666
667 def global_matches(self, text):
667 def global_matches(self, text):
668 """Compute matches when text is a simple name.
668 """Compute matches when text is a simple name.
669
669
670 Return a list of all keywords, built-in functions and names currently
670 Return a list of all keywords, built-in functions and names currently
671 defined in self.namespace or self.global_namespace that match.
671 defined in self.namespace or self.global_namespace that match.
672
672
673 """
673 """
674 matches = []
674 matches = []
675 match_append = matches.append
675 match_append = matches.append
676 n = len(text)
676 n = len(text)
677 for lst in [keyword.kwlist,
677 for lst in [keyword.kwlist,
678 builtin_mod.__dict__.keys(),
678 builtin_mod.__dict__.keys(),
679 self.namespace.keys(),
679 self.namespace.keys(),
680 self.global_namespace.keys()]:
680 self.global_namespace.keys()]:
681 for word in lst:
681 for word in lst:
682 if word[:n] == text and word != "__builtins__":
682 if word[:n] == text and word != "__builtins__":
683 match_append(word)
683 match_append(word)
684
684
685 snake_case_re = re.compile(r"[^_]+(_[^_]+)+?\Z")
685 snake_case_re = re.compile(r"[^_]+(_[^_]+)+?\Z")
686 for lst in [self.namespace.keys(),
686 for lst in [self.namespace.keys(),
687 self.global_namespace.keys()]:
687 self.global_namespace.keys()]:
688 shortened = {"_".join([sub[0] for sub in word.split('_')]) : word
688 shortened = {"_".join([sub[0] for sub in word.split('_')]) : word
689 for word in lst if snake_case_re.match(word)}
689 for word in lst if snake_case_re.match(word)}
690 for word in shortened.keys():
690 for word in shortened.keys():
691 if word[:n] == text and word != "__builtins__":
691 if word[:n] == text and word != "__builtins__":
692 match_append(shortened[word])
692 match_append(shortened[word])
693 return matches
693 return matches
694
694
695 def attr_matches(self, text):
695 def attr_matches(self, text):
696 """Compute matches when text contains a dot.
696 """Compute matches when text contains a dot.
697
697
698 Assuming the text is of the form NAME.NAME....[NAME], and is
698 Assuming the text is of the form NAME.NAME....[NAME], and is
699 evaluatable in self.namespace or self.global_namespace, it will be
699 evaluatable in self.namespace or self.global_namespace, it will be
700 evaluated and its attributes (as revealed by dir()) are used as
700 evaluated and its attributes (as revealed by dir()) are used as
701 possible completions. (For class instances, class members are
701 possible completions. (For class instances, class members are
702 also considered.)
702 also considered.)
703
703
704 WARNING: this can still invoke arbitrary C code, if an object
704 WARNING: this can still invoke arbitrary C code, if an object
705 with a __getattr__ hook is evaluated.
705 with a __getattr__ hook is evaluated.
706
706
707 """
707 """
708
708
709 # Another option, seems to work great. Catches things like ''.<tab>
709 # Another option, seems to work great. Catches things like ''.<tab>
710 m = re.match(r"(\S+(\.\w+)*)\.(\w*)$", text)
710 m = re.match(r"(\S+(\.\w+)*)\.(\w*)$", text)
711
711
712 if m:
712 if m:
713 expr, attr = m.group(1, 3)
713 expr, attr = m.group(1, 3)
714 elif self.greedy:
714 elif self.greedy:
715 m2 = re.match(r"(.+)\.(\w*)$", self.line_buffer)
715 m2 = re.match(r"(.+)\.(\w*)$", self.line_buffer)
716 if not m2:
716 if not m2:
717 return []
717 return []
718 expr, attr = m2.group(1,2)
718 expr, attr = m2.group(1,2)
719 else:
719 else:
720 return []
720 return []
721
721
722 try:
722 try:
723 obj = eval(expr, self.namespace)
723 obj = eval(expr, self.namespace)
724 except:
724 except:
725 try:
725 try:
726 obj = eval(expr, self.global_namespace)
726 obj = eval(expr, self.global_namespace)
727 except:
727 except:
728 return []
728 return []
729
729
730 if self.limit_to__all__ and hasattr(obj, '__all__'):
730 if self.limit_to__all__ and hasattr(obj, '__all__'):
731 words = get__all__entries(obj)
731 words = get__all__entries(obj)
732 else:
732 else:
733 words = dir2(obj)
733 words = dir2(obj)
734
734
735 try:
735 try:
736 words = generics.complete_object(obj, words)
736 words = generics.complete_object(obj, words)
737 except TryNext:
737 except TryNext:
738 pass
738 pass
739 except AssertionError:
739 except AssertionError:
740 raise
740 raise
741 except Exception:
741 except Exception:
742 # Silence errors from completion function
742 # Silence errors from completion function
743 #raise # dbg
743 #raise # dbg
744 pass
744 pass
745 # Build match list to return
745 # Build match list to return
746 n = len(attr)
746 n = len(attr)
747 return [u"%s.%s" % (expr, w) for w in words if w[:n] == attr ]
747 return [u"%s.%s" % (expr, w) for w in words if w[:n] == attr ]
748
748
749
749
750 def get__all__entries(obj):
750 def get__all__entries(obj):
751 """returns the strings in the __all__ attribute"""
751 """returns the strings in the __all__ attribute"""
752 try:
752 try:
753 words = getattr(obj, '__all__')
753 words = getattr(obj, '__all__')
754 except:
754 except:
755 return []
755 return []
756
756
757 return [w for w in words if isinstance(w, str)]
757 return [w for w in words if isinstance(w, str)]
758
758
759
759
760 def match_dict_keys(keys: List[Union[str, bytes, Tuple[Union[str, bytes]]]], prefix: str, delims: str,
760 def match_dict_keys(keys: List[Union[str, bytes, Tuple[Union[str, bytes]]]], prefix: str, delims: str,
761 extra_prefix: Optional[Tuple[str, bytes]]=None) -> Tuple[str, int, List[str]]:
761 extra_prefix: Optional[Tuple[str, bytes]]=None) -> Tuple[str, int, List[str]]:
762 """Used by dict_key_matches, matching the prefix to a list of keys
762 """Used by dict_key_matches, matching the prefix to a list of keys
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
774 `mydict['foo', "bar", 'b`, this would be `('foo', 'bar')`.
774 `mydict['foo', "bar", 'b`, this would be `('foo', 'bar')`.
775
775
776 Returns
776 Returns
777 -------
777 -------
778 A tuple of three elements: ``quote``, ``token_start``, ``matched``, with
778 A tuple of three elements: ``quote``, ``token_start``, ``matched``, with
779 ``quote`` being the quote that need to be used to close current string.
779 ``quote`` being the quote that need to be used to close current string.
780 ``token_start`` the position where the replacement should start occurring,
780 ``token_start`` the position where the replacement should start occurring,
781 ``matches`` a list of replacement/completion
781 ``matches`` a list of replacement/completion
782
782
783 """
783 """
784 prefix_tuple = extra_prefix if extra_prefix else ()
784 prefix_tuple = extra_prefix if extra_prefix else ()
785 Nprefix = len(prefix_tuple)
785 Nprefix = len(prefix_tuple)
786 def filter_prefix_tuple(key):
786 def filter_prefix_tuple(key):
787 # Reject too short keys
787 # Reject too short keys
788 if len(key) <= Nprefix:
788 if len(key) <= Nprefix:
789 return False
789 return False
790 # Reject keys with non str/bytes in it
790 # Reject keys with non str/bytes in it
791 for k in key:
791 for k in key:
792 if not isinstance(k, (str, bytes)):
792 if not isinstance(k, (str, bytes)):
793 return False
793 return False
794 # Reject keys that do not match the prefix
794 # Reject keys that do not match the prefix
795 for k, pt in zip(key, prefix_tuple):
795 for k, pt in zip(key, prefix_tuple):
796 if k != pt:
796 if k != pt:
797 return False
797 return False
798 # All checks passed!
798 # All checks passed!
799 return True
799 return True
800
800
801 filtered_keys:List[Union[str,bytes]] = []
801 filtered_keys:List[Union[str,bytes]] = []
802 def _add_to_filtered_keys(key):
802 def _add_to_filtered_keys(key):
803 if isinstance(key, (str, bytes)):
803 if isinstance(key, (str, bytes)):
804 filtered_keys.append(key)
804 filtered_keys.append(key)
805
805
806 for k in keys:
806 for k in keys:
807 if isinstance(k, tuple):
807 if isinstance(k, tuple):
808 if filter_prefix_tuple(k):
808 if filter_prefix_tuple(k):
809 _add_to_filtered_keys(k[Nprefix])
809 _add_to_filtered_keys(k[Nprefix])
810 else:
810 else:
811 _add_to_filtered_keys(k)
811 _add_to_filtered_keys(k)
812
812
813 if not prefix:
813 if not prefix:
814 return '', 0, [repr(k) for k in filtered_keys]
814 return '', 0, [repr(k) for k in filtered_keys]
815 quote_match = re.search('["\']', prefix)
815 quote_match = re.search('["\']', prefix)
816 assert quote_match is not None # silence mypy
816 assert quote_match is not None # silence mypy
817 quote = quote_match.group()
817 quote = quote_match.group()
818 try:
818 try:
819 prefix_str = eval(prefix + quote, {})
819 prefix_str = eval(prefix + quote, {})
820 except Exception:
820 except Exception:
821 return '', 0, []
821 return '', 0, []
822
822
823 pattern = '[^' + ''.join('\\' + c for c in delims) + ']*$'
823 pattern = '[^' + ''.join('\\' + c for c in delims) + ']*$'
824 token_match = re.search(pattern, prefix, re.UNICODE)
824 token_match = re.search(pattern, prefix, re.UNICODE)
825 assert token_match is not None # silence mypy
825 assert token_match is not None # silence mypy
826 token_start = token_match.start()
826 token_start = token_match.start()
827 token_prefix = token_match.group()
827 token_prefix = token_match.group()
828
828
829 matched:List[str] = []
829 matched:List[str] = []
830 for key in filtered_keys:
830 for key in filtered_keys:
831 try:
831 try:
832 if not key.startswith(prefix_str):
832 if not key.startswith(prefix_str):
833 continue
833 continue
834 except (AttributeError, TypeError, UnicodeError):
834 except (AttributeError, TypeError, UnicodeError):
835 # Python 3+ TypeError on b'a'.startswith('a') or vice-versa
835 # Python 3+ TypeError on b'a'.startswith('a') or vice-versa
836 continue
836 continue
837
837
838 # reformat remainder of key to begin with prefix
838 # reformat remainder of key to begin with prefix
839 rem = key[len(prefix_str):]
839 rem = key[len(prefix_str):]
840 # force repr wrapped in '
840 # force repr wrapped in '
841 rem_repr = repr(rem + '"') if isinstance(rem, str) else repr(rem + b'"')
841 rem_repr = repr(rem + '"') if isinstance(rem, str) else repr(rem + b'"')
842 rem_repr = rem_repr[1 + rem_repr.index("'"):-2]
842 rem_repr = rem_repr[1 + rem_repr.index("'"):-2]
843 if quote == '"':
843 if quote == '"':
844 # The entered prefix is quoted with ",
844 # The entered prefix is quoted with ",
845 # but the match is quoted with '.
845 # but the match is quoted with '.
846 # A contained " hence needs escaping for comparison:
846 # A contained " hence needs escaping for comparison:
847 rem_repr = rem_repr.replace('"', '\\"')
847 rem_repr = rem_repr.replace('"', '\\"')
848
848
849 # then reinsert prefix from start of token
849 # then reinsert prefix from start of token
850 matched.append('%s%s' % (token_prefix, rem_repr))
850 matched.append('%s%s' % (token_prefix, rem_repr))
851 return quote, token_start, matched
851 return quote, token_start, matched
852
852
853
853
854 def cursor_to_position(text:str, line:int, column:int)->int:
854 def cursor_to_position(text:str, line:int, column:int)->int:
855 """
855 """
856 Convert the (line,column) position of the cursor in text to an offset in a
856 Convert the (line,column) position of the cursor in text to an offset in a
857 string.
857 string.
858
858
859 Parameters
859 Parameters
860 ----------
860 ----------
861 text : str
861 text : str
862 The text in which to calculate the cursor offset
862 The text in which to calculate the cursor offset
863 line : int
863 line : int
864 Line of the cursor; 0-indexed
864 Line of the cursor; 0-indexed
865 column : int
865 column : int
866 Column of the cursor 0-indexed
866 Column of the cursor 0-indexed
867
867
868 Returns
868 Returns
869 -------
869 -------
870 Position of the cursor in ``text``, 0-indexed.
870 Position of the cursor in ``text``, 0-indexed.
871
871
872 See Also
872 See Also
873 --------
873 --------
874 position_to_cursor : reciprocal of this function
874 position_to_cursor : reciprocal of this function
875
875
876 """
876 """
877 lines = text.split('\n')
877 lines = text.split('\n')
878 assert line <= len(lines), '{} <= {}'.format(str(line), str(len(lines)))
878 assert line <= len(lines), '{} <= {}'.format(str(line), str(len(lines)))
879
879
880 return sum(len(l) + 1 for l in lines[:line]) + column
880 return sum(len(l) + 1 for l in lines[:line]) + column
881
881
882 def position_to_cursor(text:str, offset:int)->Tuple[int, int]:
882 def position_to_cursor(text:str, offset:int)->Tuple[int, int]:
883 """
883 """
884 Convert the position of the cursor in text (0 indexed) to a line
884 Convert the position of the cursor in text (0 indexed) to a line
885 number(0-indexed) and a column number (0-indexed) pair
885 number(0-indexed) and a column number (0-indexed) pair
886
886
887 Position should be a valid position in ``text``.
887 Position should be a valid position in ``text``.
888
888
889 Parameters
889 Parameters
890 ----------
890 ----------
891 text : str
891 text : str
892 The text in which to calculate the cursor offset
892 The text in which to calculate the cursor offset
893 offset : int
893 offset : int
894 Position of the cursor in ``text``, 0-indexed.
894 Position of the cursor in ``text``, 0-indexed.
895
895
896 Returns
896 Returns
897 -------
897 -------
898 (line, column) : (int, int)
898 (line, column) : (int, int)
899 Line of the cursor; 0-indexed, column of the cursor 0-indexed
899 Line of the cursor; 0-indexed, column of the cursor 0-indexed
900
900
901 See Also
901 See Also
902 --------
902 --------
903 cursor_to_position : reciprocal of this function
903 cursor_to_position : reciprocal of this function
904
904
905 """
905 """
906
906
907 assert 0 <= offset <= len(text) , "0 <= %s <= %s" % (offset , len(text))
907 assert 0 <= offset <= len(text) , "0 <= %s <= %s" % (offset , len(text))
908
908
909 before = text[:offset]
909 before = text[:offset]
910 blines = before.split('\n') # ! splitnes trim trailing \n
910 blines = before.split('\n') # ! splitnes trim trailing \n
911 line = before.count('\n')
911 line = before.count('\n')
912 col = len(blines[-1])
912 col = len(blines[-1])
913 return line, col
913 return line, col
914
914
915
915
916 def _safe_isinstance(obj, module, class_name):
916 def _safe_isinstance(obj, module, class_name):
917 """Checks if obj is an instance of module.class_name if loaded
917 """Checks if obj is an instance of module.class_name if loaded
918 """
918 """
919 return (module in sys.modules and
919 return (module in sys.modules and
920 isinstance(obj, getattr(import_module(module), class_name)))
920 isinstance(obj, getattr(import_module(module), class_name)))
921
921
922 def back_unicode_name_matches(text:str) -> Tuple[str, Sequence[str]]:
922 def back_unicode_name_matches(text:str) -> Tuple[str, Sequence[str]]:
923 """Match Unicode characters back to Unicode name
923 """Match Unicode characters back to Unicode name
924
924
925 This does ``☃`` -> ``\\snowman``
925 This does ``☃`` -> ``\\snowman``
926
926
927 Note that snowman is not a valid python3 combining character but will be expanded.
927 Note that snowman is not a valid python3 combining character but will be expanded.
928 Though it will not recombine back to the snowman character by the completion machinery.
928 Though it will not recombine back to the snowman character by the completion machinery.
929
929
930 This will not either back-complete standard sequences like \\n, \\b ...
930 This will not either back-complete standard sequences like \\n, \\b ...
931
931
932 Returns
932 Returns
933 =======
933 =======
934
934
935 Return a tuple with two elements:
935 Return a tuple with two elements:
936
936
937 - The Unicode character that was matched (preceded with a backslash), or
937 - The Unicode character that was matched (preceded with a backslash), or
938 empty string,
938 empty string,
939 - a sequence (of 1), name for the match Unicode character, preceded by
939 - a sequence (of 1), name for the match Unicode character, preceded by
940 backslash, or empty if no match.
940 backslash, or empty if no match.
941
941
942 """
942 """
943 if len(text)<2:
943 if len(text)<2:
944 return '', ()
944 return '', ()
945 maybe_slash = text[-2]
945 maybe_slash = text[-2]
946 if maybe_slash != '\\':
946 if maybe_slash != '\\':
947 return '', ()
947 return '', ()
948
948
949 char = text[-1]
949 char = text[-1]
950 # no expand on quote for completion in strings.
950 # no expand on quote for completion in strings.
951 # nor backcomplete standard ascii keys
951 # nor backcomplete standard ascii keys
952 if char in string.ascii_letters or char in ('"',"'"):
952 if char in string.ascii_letters or char in ('"',"'"):
953 return '', ()
953 return '', ()
954 try :
954 try :
955 unic = unicodedata.name(char)
955 unic = unicodedata.name(char)
956 return '\\'+char,('\\'+unic,)
956 return '\\'+char,('\\'+unic,)
957 except KeyError:
957 except KeyError:
958 pass
958 pass
959 return '', ()
959 return '', ()
960
960
961 def back_latex_name_matches(text:str) -> Tuple[str, Sequence[str]] :
961 def back_latex_name_matches(text:str) -> Tuple[str, Sequence[str]] :
962 """Match latex characters back to unicode name
962 """Match latex characters back to unicode name
963
963
964 This does ``\\ℵ`` -> ``\\aleph``
964 This does ``\\ℵ`` -> ``\\aleph``
965
965
966 """
966 """
967 if len(text)<2:
967 if len(text)<2:
968 return '', ()
968 return '', ()
969 maybe_slash = text[-2]
969 maybe_slash = text[-2]
970 if maybe_slash != '\\':
970 if maybe_slash != '\\':
971 return '', ()
971 return '', ()
972
972
973
973
974 char = text[-1]
974 char = text[-1]
975 # no expand on quote for completion in strings.
975 # no expand on quote for completion in strings.
976 # nor backcomplete standard ascii keys
976 # nor backcomplete standard ascii keys
977 if char in string.ascii_letters or char in ('"',"'"):
977 if char in string.ascii_letters or char in ('"',"'"):
978 return '', ()
978 return '', ()
979 try :
979 try :
980 latex = reverse_latex_symbol[char]
980 latex = reverse_latex_symbol[char]
981 # '\\' replace the \ as well
981 # '\\' replace the \ as well
982 return '\\'+char,[latex]
982 return '\\'+char,[latex]
983 except KeyError:
983 except KeyError:
984 pass
984 pass
985 return '', ()
985 return '', ()
986
986
987
987
988 def _formatparamchildren(parameter) -> str:
988 def _formatparamchildren(parameter) -> str:
989 """
989 """
990 Get parameter name and value from Jedi Private API
990 Get parameter name and value from Jedi Private API
991
991
992 Jedi does not expose a simple way to get `param=value` from its API.
992 Jedi does not expose a simple way to get `param=value` from its API.
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
1000 -------
1000 -------
1001 A string like 'a', 'b=1', '*args', '**kwargs'
1001 A string like 'a', 'b=1', '*args', '**kwargs'
1002
1002
1003 """
1003 """
1004 description = parameter.description
1004 description = parameter.description
1005 if not description.startswith('param '):
1005 if not description.startswith('param '):
1006 raise ValueError('Jedi function parameter description have change format.'
1006 raise ValueError('Jedi function parameter description have change format.'
1007 'Expected "param ...", found %r".' % description)
1007 'Expected "param ...", found %r".' % description)
1008 return description[6:]
1008 return description[6:]
1009
1009
1010 def _make_signature(completion)-> str:
1010 def _make_signature(completion)-> str:
1011 """
1011 """
1012 Make the signature from a jedi completion
1012 Make the signature from a jedi completion
1013
1013
1014 Parameters
1014 Parameters
1015 ----------
1015 ----------
1016 completion: jedi.Completion
1016 completion : jedi.Completion
1017 object does not complete a function type
1017 object does not complete a function type
1018
1018
1019 Returns
1019 Returns
1020 -------
1020 -------
1021 a string consisting of the function signature, with the parenthesis but
1021 a string consisting of the function signature, with the parenthesis but
1022 without the function name. example:
1022 without the function name. example:
1023 `(a, *args, b=1, **kwargs)`
1023 `(a, *args, b=1, **kwargs)`
1024
1024
1025 """
1025 """
1026
1026
1027 # it looks like this might work on jedi 0.17
1027 # it looks like this might work on jedi 0.17
1028 if hasattr(completion, 'get_signatures'):
1028 if hasattr(completion, 'get_signatures'):
1029 signatures = completion.get_signatures()
1029 signatures = completion.get_signatures()
1030 if not signatures:
1030 if not signatures:
1031 return '(?)'
1031 return '(?)'
1032
1032
1033 c0 = completion.get_signatures()[0]
1033 c0 = completion.get_signatures()[0]
1034 return '('+c0.to_string().split('(', maxsplit=1)[1]
1034 return '('+c0.to_string().split('(', maxsplit=1)[1]
1035
1035
1036 return '(%s)'% ', '.join([f for f in (_formatparamchildren(p) for signature in completion.get_signatures()
1036 return '(%s)'% ', '.join([f for f in (_formatparamchildren(p) for signature in completion.get_signatures()
1037 for p in signature.defined_names()) if f])
1037 for p in signature.defined_names()) if f])
1038
1038
1039
1039
1040 class _CompleteResult(NamedTuple):
1040 class _CompleteResult(NamedTuple):
1041 matched_text : str
1041 matched_text : str
1042 matches: Sequence[str]
1042 matches: Sequence[str]
1043 matches_origin: Sequence[str]
1043 matches_origin: Sequence[str]
1044 jedi_matches: Any
1044 jedi_matches: Any
1045
1045
1046
1046
1047 class IPCompleter(Completer):
1047 class IPCompleter(Completer):
1048 """Extension of the completer class with IPython-specific features"""
1048 """Extension of the completer class with IPython-specific features"""
1049
1049
1050 __dict_key_regexps: Optional[Dict[bool,Pattern]] = None
1050 __dict_key_regexps: Optional[Dict[bool,Pattern]] = None
1051
1051
1052 @observe('greedy')
1052 @observe('greedy')
1053 def _greedy_changed(self, change):
1053 def _greedy_changed(self, change):
1054 """update the splitter and readline delims when greedy is changed"""
1054 """update the splitter and readline delims when greedy is changed"""
1055 if change['new']:
1055 if change['new']:
1056 self.splitter.delims = GREEDY_DELIMS
1056 self.splitter.delims = GREEDY_DELIMS
1057 else:
1057 else:
1058 self.splitter.delims = DELIMS
1058 self.splitter.delims = DELIMS
1059
1059
1060 dict_keys_only = Bool(False,
1060 dict_keys_only = Bool(False,
1061 help="""Whether to show dict key matches only""")
1061 help="""Whether to show dict key matches only""")
1062
1062
1063 merge_completions = Bool(True,
1063 merge_completions = Bool(True,
1064 help="""Whether to merge completion results into a single list
1064 help="""Whether to merge completion results into a single list
1065
1065
1066 If False, only the completion results from the first non-empty
1066 If False, only the completion results from the first non-empty
1067 completer will be returned.
1067 completer will be returned.
1068 """
1068 """
1069 ).tag(config=True)
1069 ).tag(config=True)
1070 omit__names = Enum((0,1,2), default_value=2,
1070 omit__names = Enum((0,1,2), default_value=2,
1071 help="""Instruct the completer to omit private method names
1071 help="""Instruct the completer to omit private method names
1072
1072
1073 Specifically, when completing on ``object.<tab>``.
1073 Specifically, when completing on ``object.<tab>``.
1074
1074
1075 When 2 [default]: all names that start with '_' will be excluded.
1075 When 2 [default]: all names that start with '_' will be excluded.
1076
1076
1077 When 1: all 'magic' names (``__foo__``) will be excluded.
1077 When 1: all 'magic' names (``__foo__``) will be excluded.
1078
1078
1079 When 0: nothing will be excluded.
1079 When 0: nothing will be excluded.
1080 """
1080 """
1081 ).tag(config=True)
1081 ).tag(config=True)
1082 limit_to__all__ = Bool(False,
1082 limit_to__all__ = Bool(False,
1083 help="""
1083 help="""
1084 DEPRECATED as of version 5.0.
1084 DEPRECATED as of version 5.0.
1085
1085
1086 Instruct the completer to use __all__ for the completion
1086 Instruct the completer to use __all__ for the completion
1087
1087
1088 Specifically, when completing on ``object.<tab>``.
1088 Specifically, when completing on ``object.<tab>``.
1089
1089
1090 When True: only those names in obj.__all__ will be included.
1090 When True: only those names in obj.__all__ will be included.
1091
1091
1092 When False [default]: the __all__ attribute is ignored
1092 When False [default]: the __all__ attribute is ignored
1093 """,
1093 """,
1094 ).tag(config=True)
1094 ).tag(config=True)
1095
1095
1096 profile_completions = Bool(
1096 profile_completions = Bool(
1097 default_value=False,
1097 default_value=False,
1098 help="If True, emit profiling data for completion subsystem using cProfile."
1098 help="If True, emit profiling data for completion subsystem using cProfile."
1099 ).tag(config=True)
1099 ).tag(config=True)
1100
1100
1101 profiler_output_dir = Unicode(
1101 profiler_output_dir = Unicode(
1102 default_value=".completion_profiles",
1102 default_value=".completion_profiles",
1103 help="Template for path at which to output profile data for completions."
1103 help="Template for path at which to output profile data for completions."
1104 ).tag(config=True)
1104 ).tag(config=True)
1105
1105
1106 @observe('limit_to__all__')
1106 @observe('limit_to__all__')
1107 def _limit_to_all_changed(self, change):
1107 def _limit_to_all_changed(self, change):
1108 warnings.warn('`IPython.core.IPCompleter.limit_to__all__` configuration '
1108 warnings.warn('`IPython.core.IPCompleter.limit_to__all__` configuration '
1109 'value has been deprecated since IPython 5.0, will be made to have '
1109 'value has been deprecated since IPython 5.0, will be made to have '
1110 'no effects and then removed in future version of IPython.',
1110 'no effects and then removed in future version of IPython.',
1111 UserWarning)
1111 UserWarning)
1112
1112
1113 def __init__(self, shell=None, namespace=None, global_namespace=None,
1113 def __init__(self, shell=None, namespace=None, global_namespace=None,
1114 use_readline=_deprecation_readline_sentinel, config=None, **kwargs):
1114 use_readline=_deprecation_readline_sentinel, config=None, **kwargs):
1115 """IPCompleter() -> completer
1115 """IPCompleter() -> completer
1116
1116
1117 Return a completer object.
1117 Return a completer object.
1118
1118
1119 Parameters
1119 Parameters
1120 ----------
1120 ----------
1121 shell
1121 shell
1122 a pointer to the ipython shell itself. This is needed
1122 a pointer to the ipython shell itself. This is needed
1123 because this completer knows about magic functions, and those can
1123 because this completer knows about magic functions, and those can
1124 only be accessed via the ipython instance.
1124 only be accessed via the ipython instance.
1125 namespace : dict, optional
1125 namespace : dict, optional
1126 an optional dict where completions are performed.
1126 an optional dict where completions are performed.
1127 global_namespace : dict, optional
1127 global_namespace : dict, optional
1128 secondary optional dict for completions, to
1128 secondary optional dict for completions, to
1129 handle cases (such as IPython embedded inside functions) where
1129 handle cases (such as IPython embedded inside functions) where
1130 both Python scopes are visible.
1130 both Python scopes are visible.
1131 use_readline : bool, optional
1131 use_readline : bool, optional
1132 DEPRECATED, ignored since IPython 6.0, will have no effects
1132 DEPRECATED, ignored since IPython 6.0, will have no effects
1133 """
1133 """
1134
1134
1135 self.magic_escape = ESC_MAGIC
1135 self.magic_escape = ESC_MAGIC
1136 self.splitter = CompletionSplitter()
1136 self.splitter = CompletionSplitter()
1137
1137
1138 if use_readline is not _deprecation_readline_sentinel:
1138 if use_readline is not _deprecation_readline_sentinel:
1139 warnings.warn('The `use_readline` parameter is deprecated and ignored since IPython 6.0.',
1139 warnings.warn('The `use_readline` parameter is deprecated and ignored since IPython 6.0.',
1140 DeprecationWarning, stacklevel=2)
1140 DeprecationWarning, stacklevel=2)
1141
1141
1142 # _greedy_changed() depends on splitter and readline being defined:
1142 # _greedy_changed() depends on splitter and readline being defined:
1143 Completer.__init__(self, namespace=namespace, global_namespace=global_namespace,
1143 Completer.__init__(self, namespace=namespace, global_namespace=global_namespace,
1144 config=config, **kwargs)
1144 config=config, **kwargs)
1145
1145
1146 # List where completion matches will be stored
1146 # List where completion matches will be stored
1147 self.matches = []
1147 self.matches = []
1148 self.shell = shell
1148 self.shell = shell
1149 # Regexp to split filenames with spaces in them
1149 # Regexp to split filenames with spaces in them
1150 self.space_name_re = re.compile(r'([^\\] )')
1150 self.space_name_re = re.compile(r'([^\\] )')
1151 # Hold a local ref. to glob.glob for speed
1151 # Hold a local ref. to glob.glob for speed
1152 self.glob = glob.glob
1152 self.glob = glob.glob
1153
1153
1154 # Determine if we are running on 'dumb' terminals, like (X)Emacs
1154 # Determine if we are running on 'dumb' terminals, like (X)Emacs
1155 # buffers, to avoid completion problems.
1155 # buffers, to avoid completion problems.
1156 term = os.environ.get('TERM','xterm')
1156 term = os.environ.get('TERM','xterm')
1157 self.dumb_terminal = term in ['dumb','emacs']
1157 self.dumb_terminal = term in ['dumb','emacs']
1158
1158
1159 # Special handling of backslashes needed in win32 platforms
1159 # Special handling of backslashes needed in win32 platforms
1160 if sys.platform == "win32":
1160 if sys.platform == "win32":
1161 self.clean_glob = self._clean_glob_win32
1161 self.clean_glob = self._clean_glob_win32
1162 else:
1162 else:
1163 self.clean_glob = self._clean_glob
1163 self.clean_glob = self._clean_glob
1164
1164
1165 #regexp to parse docstring for function signature
1165 #regexp to parse docstring for function signature
1166 self.docstring_sig_re = re.compile(r'^[\w|\s.]+\(([^)]*)\).*')
1166 self.docstring_sig_re = re.compile(r'^[\w|\s.]+\(([^)]*)\).*')
1167 self.docstring_kwd_re = re.compile(r'[\s|\[]*(\w+)(?:\s*=\s*.*)')
1167 self.docstring_kwd_re = re.compile(r'[\s|\[]*(\w+)(?:\s*=\s*.*)')
1168 #use this if positional argument name is also needed
1168 #use this if positional argument name is also needed
1169 #= re.compile(r'[\s|\[]*(\w+)(?:\s*=?\s*.*)')
1169 #= re.compile(r'[\s|\[]*(\w+)(?:\s*=?\s*.*)')
1170
1170
1171 self.magic_arg_matchers = [
1171 self.magic_arg_matchers = [
1172 self.magic_config_matches,
1172 self.magic_config_matches,
1173 self.magic_color_matches,
1173 self.magic_color_matches,
1174 ]
1174 ]
1175
1175
1176 # This is set externally by InteractiveShell
1176 # This is set externally by InteractiveShell
1177 self.custom_completers = None
1177 self.custom_completers = None
1178
1178
1179 # This is a list of names of unicode characters that can be completed
1179 # This is a list of names of unicode characters that can be completed
1180 # into their corresponding unicode value. The list is large, so we
1180 # into their corresponding unicode value. The list is large, so we
1181 # laziliy initialize it on first use. Consuming code should access this
1181 # laziliy initialize it on first use. Consuming code should access this
1182 # attribute through the `@unicode_names` property.
1182 # attribute through the `@unicode_names` property.
1183 self._unicode_names = None
1183 self._unicode_names = None
1184
1184
1185 @property
1185 @property
1186 def matchers(self) -> List[Any]:
1186 def matchers(self) -> List[Any]:
1187 """All active matcher routines for completion"""
1187 """All active matcher routines for completion"""
1188 if self.dict_keys_only:
1188 if self.dict_keys_only:
1189 return [self.dict_key_matches]
1189 return [self.dict_key_matches]
1190
1190
1191 if self.use_jedi:
1191 if self.use_jedi:
1192 return [
1192 return [
1193 *self.custom_matchers,
1193 *self.custom_matchers,
1194 self.file_matches,
1194 self.file_matches,
1195 self.magic_matches,
1195 self.magic_matches,
1196 self.dict_key_matches,
1196 self.dict_key_matches,
1197 ]
1197 ]
1198 else:
1198 else:
1199 return [
1199 return [
1200 *self.custom_matchers,
1200 *self.custom_matchers,
1201 self.python_matches,
1201 self.python_matches,
1202 self.file_matches,
1202 self.file_matches,
1203 self.magic_matches,
1203 self.magic_matches,
1204 self.python_func_kw_matches,
1204 self.python_func_kw_matches,
1205 self.dict_key_matches,
1205 self.dict_key_matches,
1206 ]
1206 ]
1207
1207
1208 def all_completions(self, text:str) -> List[str]:
1208 def all_completions(self, text:str) -> List[str]:
1209 """
1209 """
1210 Wrapper around the completion methods for the benefit of emacs.
1210 Wrapper around the completion methods for the benefit of emacs.
1211 """
1211 """
1212 prefix = text.rpartition('.')[0]
1212 prefix = text.rpartition('.')[0]
1213 with provisionalcompleter():
1213 with provisionalcompleter():
1214 return ['.'.join([prefix, c.text]) if prefix and self.use_jedi else c.text
1214 return ['.'.join([prefix, c.text]) if prefix and self.use_jedi else c.text
1215 for c in self.completions(text, len(text))]
1215 for c in self.completions(text, len(text))]
1216
1216
1217 return self.complete(text)[1]
1217 return self.complete(text)[1]
1218
1218
1219 def _clean_glob(self, text:str):
1219 def _clean_glob(self, text:str):
1220 return self.glob("%s*" % text)
1220 return self.glob("%s*" % text)
1221
1221
1222 def _clean_glob_win32(self, text:str):
1222 def _clean_glob_win32(self, text:str):
1223 return [f.replace("\\","/")
1223 return [f.replace("\\","/")
1224 for f in self.glob("%s*" % text)]
1224 for f in self.glob("%s*" % text)]
1225
1225
1226 def file_matches(self, text:str)->List[str]:
1226 def file_matches(self, text:str)->List[str]:
1227 """Match filenames, expanding ~USER type strings.
1227 """Match filenames, expanding ~USER type strings.
1228
1228
1229 Most of the seemingly convoluted logic in this completer is an
1229 Most of the seemingly convoluted logic in this completer is an
1230 attempt to handle filenames with spaces in them. And yet it's not
1230 attempt to handle filenames with spaces in them. And yet it's not
1231 quite perfect, because Python's readline doesn't expose all of the
1231 quite perfect, because Python's readline doesn't expose all of the
1232 GNU readline details needed for this to be done correctly.
1232 GNU readline details needed for this to be done correctly.
1233
1233
1234 For a filename with a space in it, the printed completions will be
1234 For a filename with a space in it, the printed completions will be
1235 only the parts after what's already been typed (instead of the
1235 only the parts after what's already been typed (instead of the
1236 full completions, as is normally done). I don't think with the
1236 full completions, as is normally done). I don't think with the
1237 current (as of Python 2.3) Python readline it's possible to do
1237 current (as of Python 2.3) Python readline it's possible to do
1238 better."""
1238 better."""
1239
1239
1240 # chars that require escaping with backslash - i.e. chars
1240 # chars that require escaping with backslash - i.e. chars
1241 # that readline treats incorrectly as delimiters, but we
1241 # that readline treats incorrectly as delimiters, but we
1242 # don't want to treat as delimiters in filename matching
1242 # don't want to treat as delimiters in filename matching
1243 # when escaped with backslash
1243 # when escaped with backslash
1244 if text.startswith('!'):
1244 if text.startswith('!'):
1245 text = text[1:]
1245 text = text[1:]
1246 text_prefix = u'!'
1246 text_prefix = u'!'
1247 else:
1247 else:
1248 text_prefix = u''
1248 text_prefix = u''
1249
1249
1250 text_until_cursor = self.text_until_cursor
1250 text_until_cursor = self.text_until_cursor
1251 # track strings with open quotes
1251 # track strings with open quotes
1252 open_quotes = has_open_quotes(text_until_cursor)
1252 open_quotes = has_open_quotes(text_until_cursor)
1253
1253
1254 if '(' in text_until_cursor or '[' in text_until_cursor:
1254 if '(' in text_until_cursor or '[' in text_until_cursor:
1255 lsplit = text
1255 lsplit = text
1256 else:
1256 else:
1257 try:
1257 try:
1258 # arg_split ~ shlex.split, but with unicode bugs fixed by us
1258 # arg_split ~ shlex.split, but with unicode bugs fixed by us
1259 lsplit = arg_split(text_until_cursor)[-1]
1259 lsplit = arg_split(text_until_cursor)[-1]
1260 except ValueError:
1260 except ValueError:
1261 # typically an unmatched ", or backslash without escaped char.
1261 # typically an unmatched ", or backslash without escaped char.
1262 if open_quotes:
1262 if open_quotes:
1263 lsplit = text_until_cursor.split(open_quotes)[-1]
1263 lsplit = text_until_cursor.split(open_quotes)[-1]
1264 else:
1264 else:
1265 return []
1265 return []
1266 except IndexError:
1266 except IndexError:
1267 # tab pressed on empty line
1267 # tab pressed on empty line
1268 lsplit = ""
1268 lsplit = ""
1269
1269
1270 if not open_quotes and lsplit != protect_filename(lsplit):
1270 if not open_quotes and lsplit != protect_filename(lsplit):
1271 # if protectables are found, do matching on the whole escaped name
1271 # if protectables are found, do matching on the whole escaped name
1272 has_protectables = True
1272 has_protectables = True
1273 text0,text = text,lsplit
1273 text0,text = text,lsplit
1274 else:
1274 else:
1275 has_protectables = False
1275 has_protectables = False
1276 text = os.path.expanduser(text)
1276 text = os.path.expanduser(text)
1277
1277
1278 if text == "":
1278 if text == "":
1279 return [text_prefix + protect_filename(f) for f in self.glob("*")]
1279 return [text_prefix + protect_filename(f) for f in self.glob("*")]
1280
1280
1281 # Compute the matches from the filesystem
1281 # Compute the matches from the filesystem
1282 if sys.platform == 'win32':
1282 if sys.platform == 'win32':
1283 m0 = self.clean_glob(text)
1283 m0 = self.clean_glob(text)
1284 else:
1284 else:
1285 m0 = self.clean_glob(text.replace('\\', ''))
1285 m0 = self.clean_glob(text.replace('\\', ''))
1286
1286
1287 if has_protectables:
1287 if has_protectables:
1288 # If we had protectables, we need to revert our changes to the
1288 # If we had protectables, we need to revert our changes to the
1289 # beginning of filename so that we don't double-write the part
1289 # beginning of filename so that we don't double-write the part
1290 # of the filename we have so far
1290 # of the filename we have so far
1291 len_lsplit = len(lsplit)
1291 len_lsplit = len(lsplit)
1292 matches = [text_prefix + text0 +
1292 matches = [text_prefix + text0 +
1293 protect_filename(f[len_lsplit:]) for f in m0]
1293 protect_filename(f[len_lsplit:]) for f in m0]
1294 else:
1294 else:
1295 if open_quotes:
1295 if open_quotes:
1296 # if we have a string with an open quote, we don't need to
1296 # if we have a string with an open quote, we don't need to
1297 # protect the names beyond the quote (and we _shouldn't_, as
1297 # protect the names beyond the quote (and we _shouldn't_, as
1298 # it would cause bugs when the filesystem call is made).
1298 # it would cause bugs when the filesystem call is made).
1299 matches = m0 if sys.platform == "win32" else\
1299 matches = m0 if sys.platform == "win32" else\
1300 [protect_filename(f, open_quotes) for f in m0]
1300 [protect_filename(f, open_quotes) for f in m0]
1301 else:
1301 else:
1302 matches = [text_prefix +
1302 matches = [text_prefix +
1303 protect_filename(f) for f in m0]
1303 protect_filename(f) for f in m0]
1304
1304
1305 # Mark directories in input list by appending '/' to their names.
1305 # Mark directories in input list by appending '/' to their names.
1306 return [x+'/' if os.path.isdir(x) else x for x in matches]
1306 return [x+'/' if os.path.isdir(x) else x for x in matches]
1307
1307
1308 def magic_matches(self, text:str):
1308 def magic_matches(self, text:str):
1309 """Match magics"""
1309 """Match magics"""
1310 # Get all shell magics now rather than statically, so magics loaded at
1310 # Get all shell magics now rather than statically, so magics loaded at
1311 # runtime show up too.
1311 # runtime show up too.
1312 lsm = self.shell.magics_manager.lsmagic()
1312 lsm = self.shell.magics_manager.lsmagic()
1313 line_magics = lsm['line']
1313 line_magics = lsm['line']
1314 cell_magics = lsm['cell']
1314 cell_magics = lsm['cell']
1315 pre = self.magic_escape
1315 pre = self.magic_escape
1316 pre2 = pre+pre
1316 pre2 = pre+pre
1317
1317
1318 explicit_magic = text.startswith(pre)
1318 explicit_magic = text.startswith(pre)
1319
1319
1320 # Completion logic:
1320 # Completion logic:
1321 # - user gives %%: only do cell magics
1321 # - user gives %%: only do cell magics
1322 # - user gives %: do both line and cell magics
1322 # - user gives %: do both line and cell magics
1323 # - no prefix: do both
1323 # - no prefix: do both
1324 # In other words, line magics are skipped if the user gives %% explicitly
1324 # In other words, line magics are skipped if the user gives %% explicitly
1325 #
1325 #
1326 # We also exclude magics that match any currently visible names:
1326 # We also exclude magics that match any currently visible names:
1327 # https://github.com/ipython/ipython/issues/4877, unless the user has
1327 # https://github.com/ipython/ipython/issues/4877, unless the user has
1328 # typed a %:
1328 # typed a %:
1329 # https://github.com/ipython/ipython/issues/10754
1329 # https://github.com/ipython/ipython/issues/10754
1330 bare_text = text.lstrip(pre)
1330 bare_text = text.lstrip(pre)
1331 global_matches = self.global_matches(bare_text)
1331 global_matches = self.global_matches(bare_text)
1332 if not explicit_magic:
1332 if not explicit_magic:
1333 def matches(magic):
1333 def matches(magic):
1334 """
1334 """
1335 Filter magics, in particular remove magics that match
1335 Filter magics, in particular remove magics that match
1336 a name present in global namespace.
1336 a name present in global namespace.
1337 """
1337 """
1338 return ( magic.startswith(bare_text) and
1338 return ( magic.startswith(bare_text) and
1339 magic not in global_matches )
1339 magic not in global_matches )
1340 else:
1340 else:
1341 def matches(magic):
1341 def matches(magic):
1342 return magic.startswith(bare_text)
1342 return magic.startswith(bare_text)
1343
1343
1344 comp = [ pre2+m for m in cell_magics if matches(m)]
1344 comp = [ pre2+m for m in cell_magics if matches(m)]
1345 if not text.startswith(pre2):
1345 if not text.startswith(pre2):
1346 comp += [ pre+m for m in line_magics if matches(m)]
1346 comp += [ pre+m for m in line_magics if matches(m)]
1347
1347
1348 return comp
1348 return comp
1349
1349
1350 def magic_config_matches(self, text:str) -> List[str]:
1350 def magic_config_matches(self, text:str) -> List[str]:
1351 """ Match class names and attributes for %config magic """
1351 """ Match class names and attributes for %config magic """
1352 texts = text.strip().split()
1352 texts = text.strip().split()
1353
1353
1354 if len(texts) > 0 and (texts[0] == 'config' or texts[0] == '%config'):
1354 if len(texts) > 0 and (texts[0] == 'config' or texts[0] == '%config'):
1355 # get all configuration classes
1355 # get all configuration classes
1356 classes = sorted(set([ c for c in self.shell.configurables
1356 classes = sorted(set([ c for c in self.shell.configurables
1357 if c.__class__.class_traits(config=True)
1357 if c.__class__.class_traits(config=True)
1358 ]), key=lambda x: x.__class__.__name__)
1358 ]), key=lambda x: x.__class__.__name__)
1359 classnames = [ c.__class__.__name__ for c in classes ]
1359 classnames = [ c.__class__.__name__ for c in classes ]
1360
1360
1361 # return all classnames if config or %config is given
1361 # return all classnames if config or %config is given
1362 if len(texts) == 1:
1362 if len(texts) == 1:
1363 return classnames
1363 return classnames
1364
1364
1365 # match classname
1365 # match classname
1366 classname_texts = texts[1].split('.')
1366 classname_texts = texts[1].split('.')
1367 classname = classname_texts[0]
1367 classname = classname_texts[0]
1368 classname_matches = [ c for c in classnames
1368 classname_matches = [ c for c in classnames
1369 if c.startswith(classname) ]
1369 if c.startswith(classname) ]
1370
1370
1371 # return matched classes or the matched class with attributes
1371 # return matched classes or the matched class with attributes
1372 if texts[1].find('.') < 0:
1372 if texts[1].find('.') < 0:
1373 return classname_matches
1373 return classname_matches
1374 elif len(classname_matches) == 1 and \
1374 elif len(classname_matches) == 1 and \
1375 classname_matches[0] == classname:
1375 classname_matches[0] == classname:
1376 cls = classes[classnames.index(classname)].__class__
1376 cls = classes[classnames.index(classname)].__class__
1377 help = cls.class_get_help()
1377 help = cls.class_get_help()
1378 # strip leading '--' from cl-args:
1378 # strip leading '--' from cl-args:
1379 help = re.sub(re.compile(r'^--', re.MULTILINE), '', help)
1379 help = re.sub(re.compile(r'^--', re.MULTILINE), '', help)
1380 return [ attr.split('=')[0]
1380 return [ attr.split('=')[0]
1381 for attr in help.strip().splitlines()
1381 for attr in help.strip().splitlines()
1382 if attr.startswith(texts[1]) ]
1382 if attr.startswith(texts[1]) ]
1383 return []
1383 return []
1384
1384
1385 def magic_color_matches(self, text:str) -> List[str] :
1385 def magic_color_matches(self, text:str) -> List[str] :
1386 """ Match color schemes for %colors magic"""
1386 """ Match color schemes for %colors magic"""
1387 texts = text.split()
1387 texts = text.split()
1388 if text.endswith(' '):
1388 if text.endswith(' '):
1389 # .split() strips off the trailing whitespace. Add '' back
1389 # .split() strips off the trailing whitespace. Add '' back
1390 # so that: '%colors ' -> ['%colors', '']
1390 # so that: '%colors ' -> ['%colors', '']
1391 texts.append('')
1391 texts.append('')
1392
1392
1393 if len(texts) == 2 and (texts[0] == 'colors' or texts[0] == '%colors'):
1393 if len(texts) == 2 and (texts[0] == 'colors' or texts[0] == '%colors'):
1394 prefix = texts[1]
1394 prefix = texts[1]
1395 return [ color for color in InspectColors.keys()
1395 return [ color for color in InspectColors.keys()
1396 if color.startswith(prefix) ]
1396 if color.startswith(prefix) ]
1397 return []
1397 return []
1398
1398
1399 def _jedi_matches(self, cursor_column:int, cursor_line:int, text:str) -> Iterable[Any]:
1399 def _jedi_matches(self, cursor_column:int, cursor_line:int, text:str) -> Iterable[Any]:
1400 """
1400 """
1401 Return a list of :any:`jedi.api.Completions` object from a ``text`` and
1401 Return a list of :any:`jedi.api.Completions` object from a ``text`` and
1402 cursor position.
1402 cursor position.
1403
1403
1404 Parameters
1404 Parameters
1405 ----------
1405 ----------
1406 cursor_column : int
1406 cursor_column : int
1407 column position of the cursor in ``text``, 0-indexed.
1407 column position of the cursor in ``text``, 0-indexed.
1408 cursor_line : int
1408 cursor_line : int
1409 line position of the cursor in ``text``, 0-indexed
1409 line position of the cursor in ``text``, 0-indexed
1410 text : str
1410 text : str
1411 text to complete
1411 text to complete
1412
1412
1413 Notes
1413 Notes
1414 -----
1414 -----
1415 If ``IPCompleter.debug`` is ``True`` may return a :any:`_FakeJediCompletion`
1415 If ``IPCompleter.debug`` is ``True`` may return a :any:`_FakeJediCompletion`
1416 object containing a string with the Jedi debug information attached.
1416 object containing a string with the Jedi debug information attached.
1417 """
1417 """
1418 namespaces = [self.namespace]
1418 namespaces = [self.namespace]
1419 if self.global_namespace is not None:
1419 if self.global_namespace is not None:
1420 namespaces.append(self.global_namespace)
1420 namespaces.append(self.global_namespace)
1421
1421
1422 completion_filter = lambda x:x
1422 completion_filter = lambda x:x
1423 offset = cursor_to_position(text, cursor_line, cursor_column)
1423 offset = cursor_to_position(text, cursor_line, cursor_column)
1424 # filter output if we are completing for object members
1424 # filter output if we are completing for object members
1425 if offset:
1425 if offset:
1426 pre = text[offset-1]
1426 pre = text[offset-1]
1427 if pre == '.':
1427 if pre == '.':
1428 if self.omit__names == 2:
1428 if self.omit__names == 2:
1429 completion_filter = lambda c:not c.name.startswith('_')
1429 completion_filter = lambda c:not c.name.startswith('_')
1430 elif self.omit__names == 1:
1430 elif self.omit__names == 1:
1431 completion_filter = lambda c:not (c.name.startswith('__') and c.name.endswith('__'))
1431 completion_filter = lambda c:not (c.name.startswith('__') and c.name.endswith('__'))
1432 elif self.omit__names == 0:
1432 elif self.omit__names == 0:
1433 completion_filter = lambda x:x
1433 completion_filter = lambda x:x
1434 else:
1434 else:
1435 raise ValueError("Don't understand self.omit__names == {}".format(self.omit__names))
1435 raise ValueError("Don't understand self.omit__names == {}".format(self.omit__names))
1436
1436
1437 interpreter = jedi.Interpreter(text[:offset], namespaces)
1437 interpreter = jedi.Interpreter(text[:offset], namespaces)
1438 try_jedi = True
1438 try_jedi = True
1439
1439
1440 try:
1440 try:
1441 # find the first token in the current tree -- if it is a ' or " then we are in a string
1441 # find the first token in the current tree -- if it is a ' or " then we are in a string
1442 completing_string = False
1442 completing_string = False
1443 try:
1443 try:
1444 first_child = next(c for c in interpreter._get_module().tree_node.children if hasattr(c, 'value'))
1444 first_child = next(c for c in interpreter._get_module().tree_node.children if hasattr(c, 'value'))
1445 except StopIteration:
1445 except StopIteration:
1446 pass
1446 pass
1447 else:
1447 else:
1448 # note the value may be ', ", or it may also be ''' or """, or
1448 # note the value may be ', ", or it may also be ''' or """, or
1449 # in some cases, """what/you/typed..., but all of these are
1449 # in some cases, """what/you/typed..., but all of these are
1450 # strings.
1450 # strings.
1451 completing_string = len(first_child.value) > 0 and first_child.value[0] in {"'", '"'}
1451 completing_string = len(first_child.value) > 0 and first_child.value[0] in {"'", '"'}
1452
1452
1453 # if we are in a string jedi is likely not the right candidate for
1453 # if we are in a string jedi is likely not the right candidate for
1454 # now. Skip it.
1454 # now. Skip it.
1455 try_jedi = not completing_string
1455 try_jedi = not completing_string
1456 except Exception as e:
1456 except Exception as e:
1457 # many of things can go wrong, we are using private API just don't crash.
1457 # many of things can go wrong, we are using private API just don't crash.
1458 if self.debug:
1458 if self.debug:
1459 print("Error detecting if completing a non-finished string :", e, '|')
1459 print("Error detecting if completing a non-finished string :", e, '|')
1460
1460
1461 if not try_jedi:
1461 if not try_jedi:
1462 return []
1462 return []
1463 try:
1463 try:
1464 return filter(completion_filter, interpreter.complete(column=cursor_column, line=cursor_line + 1))
1464 return filter(completion_filter, interpreter.complete(column=cursor_column, line=cursor_line + 1))
1465 except Exception as e:
1465 except Exception as e:
1466 if self.debug:
1466 if self.debug:
1467 return [_FakeJediCompletion('Oops Jedi has crashed, please report a bug with the following:\n"""\n%s\ns"""' % (e))]
1467 return [_FakeJediCompletion('Oops Jedi has crashed, please report a bug with the following:\n"""\n%s\ns"""' % (e))]
1468 else:
1468 else:
1469 return []
1469 return []
1470
1470
1471 def python_matches(self, text:str)->List[str]:
1471 def python_matches(self, text:str)->List[str]:
1472 """Match attributes or global python names"""
1472 """Match attributes or global python names"""
1473 if "." in text:
1473 if "." in text:
1474 try:
1474 try:
1475 matches = self.attr_matches(text)
1475 matches = self.attr_matches(text)
1476 if text.endswith('.') and self.omit__names:
1476 if text.endswith('.') and self.omit__names:
1477 if self.omit__names == 1:
1477 if self.omit__names == 1:
1478 # true if txt is _not_ a __ name, false otherwise:
1478 # true if txt is _not_ a __ name, false otherwise:
1479 no__name = (lambda txt:
1479 no__name = (lambda txt:
1480 re.match(r'.*\.__.*?__',txt) is None)
1480 re.match(r'.*\.__.*?__',txt) is None)
1481 else:
1481 else:
1482 # true if txt is _not_ a _ name, false otherwise:
1482 # true if txt is _not_ a _ name, false otherwise:
1483 no__name = (lambda txt:
1483 no__name = (lambda txt:
1484 re.match(r'\._.*?',txt[txt.rindex('.'):]) is None)
1484 re.match(r'\._.*?',txt[txt.rindex('.'):]) is None)
1485 matches = filter(no__name, matches)
1485 matches = filter(no__name, matches)
1486 except NameError:
1486 except NameError:
1487 # catches <undefined attributes>.<tab>
1487 # catches <undefined attributes>.<tab>
1488 matches = []
1488 matches = []
1489 else:
1489 else:
1490 matches = self.global_matches(text)
1490 matches = self.global_matches(text)
1491 return matches
1491 return matches
1492
1492
1493 def _default_arguments_from_docstring(self, doc):
1493 def _default_arguments_from_docstring(self, doc):
1494 """Parse the first line of docstring for call signature.
1494 """Parse the first line of docstring for call signature.
1495
1495
1496 Docstring should be of the form 'min(iterable[, key=func])\n'.
1496 Docstring should be of the form 'min(iterable[, key=func])\n'.
1497 It can also parse cython docstring of the form
1497 It can also parse cython docstring of the form
1498 'Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)'.
1498 'Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)'.
1499 """
1499 """
1500 if doc is None:
1500 if doc is None:
1501 return []
1501 return []
1502
1502
1503 #care only the firstline
1503 #care only the firstline
1504 line = doc.lstrip().splitlines()[0]
1504 line = doc.lstrip().splitlines()[0]
1505
1505
1506 #p = re.compile(r'^[\w|\s.]+\(([^)]*)\).*')
1506 #p = re.compile(r'^[\w|\s.]+\(([^)]*)\).*')
1507 #'min(iterable[, key=func])\n' -> 'iterable[, key=func]'
1507 #'min(iterable[, key=func])\n' -> 'iterable[, key=func]'
1508 sig = self.docstring_sig_re.search(line)
1508 sig = self.docstring_sig_re.search(line)
1509 if sig is None:
1509 if sig is None:
1510 return []
1510 return []
1511 # iterable[, key=func]' -> ['iterable[' ,' key=func]']
1511 # iterable[, key=func]' -> ['iterable[' ,' key=func]']
1512 sig = sig.groups()[0].split(',')
1512 sig = sig.groups()[0].split(',')
1513 ret = []
1513 ret = []
1514 for s in sig:
1514 for s in sig:
1515 #re.compile(r'[\s|\[]*(\w+)(?:\s*=\s*.*)')
1515 #re.compile(r'[\s|\[]*(\w+)(?:\s*=\s*.*)')
1516 ret += self.docstring_kwd_re.findall(s)
1516 ret += self.docstring_kwd_re.findall(s)
1517 return ret
1517 return ret
1518
1518
1519 def _default_arguments(self, obj):
1519 def _default_arguments(self, obj):
1520 """Return the list of default arguments of obj if it is callable,
1520 """Return the list of default arguments of obj if it is callable,
1521 or empty list otherwise."""
1521 or empty list otherwise."""
1522 call_obj = obj
1522 call_obj = obj
1523 ret = []
1523 ret = []
1524 if inspect.isbuiltin(obj):
1524 if inspect.isbuiltin(obj):
1525 pass
1525 pass
1526 elif not (inspect.isfunction(obj) or inspect.ismethod(obj)):
1526 elif not (inspect.isfunction(obj) or inspect.ismethod(obj)):
1527 if inspect.isclass(obj):
1527 if inspect.isclass(obj):
1528 #for cython embedsignature=True the constructor docstring
1528 #for cython embedsignature=True the constructor docstring
1529 #belongs to the object itself not __init__
1529 #belongs to the object itself not __init__
1530 ret += self._default_arguments_from_docstring(
1530 ret += self._default_arguments_from_docstring(
1531 getattr(obj, '__doc__', ''))
1531 getattr(obj, '__doc__', ''))
1532 # for classes, check for __init__,__new__
1532 # for classes, check for __init__,__new__
1533 call_obj = (getattr(obj, '__init__', None) or
1533 call_obj = (getattr(obj, '__init__', None) or
1534 getattr(obj, '__new__', None))
1534 getattr(obj, '__new__', None))
1535 # for all others, check if they are __call__able
1535 # for all others, check if they are __call__able
1536 elif hasattr(obj, '__call__'):
1536 elif hasattr(obj, '__call__'):
1537 call_obj = obj.__call__
1537 call_obj = obj.__call__
1538 ret += self._default_arguments_from_docstring(
1538 ret += self._default_arguments_from_docstring(
1539 getattr(call_obj, '__doc__', ''))
1539 getattr(call_obj, '__doc__', ''))
1540
1540
1541 _keeps = (inspect.Parameter.KEYWORD_ONLY,
1541 _keeps = (inspect.Parameter.KEYWORD_ONLY,
1542 inspect.Parameter.POSITIONAL_OR_KEYWORD)
1542 inspect.Parameter.POSITIONAL_OR_KEYWORD)
1543
1543
1544 try:
1544 try:
1545 sig = inspect.signature(call_obj)
1545 sig = inspect.signature(call_obj)
1546 ret.extend(k for k, v in sig.parameters.items() if
1546 ret.extend(k for k, v in sig.parameters.items() if
1547 v.kind in _keeps)
1547 v.kind in _keeps)
1548 except ValueError:
1548 except ValueError:
1549 pass
1549 pass
1550
1550
1551 return list(set(ret))
1551 return list(set(ret))
1552
1552
1553 def python_func_kw_matches(self, text):
1553 def python_func_kw_matches(self, text):
1554 """Match named parameters (kwargs) of the last open function"""
1554 """Match named parameters (kwargs) of the last open function"""
1555
1555
1556 if "." in text: # a parameter cannot be dotted
1556 if "." in text: # a parameter cannot be dotted
1557 return []
1557 return []
1558 try: regexp = self.__funcParamsRegex
1558 try: regexp = self.__funcParamsRegex
1559 except AttributeError:
1559 except AttributeError:
1560 regexp = self.__funcParamsRegex = re.compile(r'''
1560 regexp = self.__funcParamsRegex = re.compile(r'''
1561 '.*?(?<!\\)' | # single quoted strings or
1561 '.*?(?<!\\)' | # single quoted strings or
1562 ".*?(?<!\\)" | # double quoted strings or
1562 ".*?(?<!\\)" | # double quoted strings or
1563 \w+ | # identifier
1563 \w+ | # identifier
1564 \S # other characters
1564 \S # other characters
1565 ''', re.VERBOSE | re.DOTALL)
1565 ''', re.VERBOSE | re.DOTALL)
1566 # 1. find the nearest identifier that comes before an unclosed
1566 # 1. find the nearest identifier that comes before an unclosed
1567 # parenthesis before the cursor
1567 # parenthesis before the cursor
1568 # e.g. for "foo (1+bar(x), pa<cursor>,a=1)", the candidate is "foo"
1568 # e.g. for "foo (1+bar(x), pa<cursor>,a=1)", the candidate is "foo"
1569 tokens = regexp.findall(self.text_until_cursor)
1569 tokens = regexp.findall(self.text_until_cursor)
1570 iterTokens = reversed(tokens); openPar = 0
1570 iterTokens = reversed(tokens); openPar = 0
1571
1571
1572 for token in iterTokens:
1572 for token in iterTokens:
1573 if token == ')':
1573 if token == ')':
1574 openPar -= 1
1574 openPar -= 1
1575 elif token == '(':
1575 elif token == '(':
1576 openPar += 1
1576 openPar += 1
1577 if openPar > 0:
1577 if openPar > 0:
1578 # found the last unclosed parenthesis
1578 # found the last unclosed parenthesis
1579 break
1579 break
1580 else:
1580 else:
1581 return []
1581 return []
1582 # 2. Concatenate dotted names ("foo.bar" for "foo.bar(x, pa" )
1582 # 2. Concatenate dotted names ("foo.bar" for "foo.bar(x, pa" )
1583 ids = []
1583 ids = []
1584 isId = re.compile(r'\w+$').match
1584 isId = re.compile(r'\w+$').match
1585
1585
1586 while True:
1586 while True:
1587 try:
1587 try:
1588 ids.append(next(iterTokens))
1588 ids.append(next(iterTokens))
1589 if not isId(ids[-1]):
1589 if not isId(ids[-1]):
1590 ids.pop(); break
1590 ids.pop(); break
1591 if not next(iterTokens) == '.':
1591 if not next(iterTokens) == '.':
1592 break
1592 break
1593 except StopIteration:
1593 except StopIteration:
1594 break
1594 break
1595
1595
1596 # Find all named arguments already assigned to, as to avoid suggesting
1596 # Find all named arguments already assigned to, as to avoid suggesting
1597 # them again
1597 # them again
1598 usedNamedArgs = set()
1598 usedNamedArgs = set()
1599 par_level = -1
1599 par_level = -1
1600 for token, next_token in zip(tokens, tokens[1:]):
1600 for token, next_token in zip(tokens, tokens[1:]):
1601 if token == '(':
1601 if token == '(':
1602 par_level += 1
1602 par_level += 1
1603 elif token == ')':
1603 elif token == ')':
1604 par_level -= 1
1604 par_level -= 1
1605
1605
1606 if par_level != 0:
1606 if par_level != 0:
1607 continue
1607 continue
1608
1608
1609 if next_token != '=':
1609 if next_token != '=':
1610 continue
1610 continue
1611
1611
1612 usedNamedArgs.add(token)
1612 usedNamedArgs.add(token)
1613
1613
1614 argMatches = []
1614 argMatches = []
1615 try:
1615 try:
1616 callableObj = '.'.join(ids[::-1])
1616 callableObj = '.'.join(ids[::-1])
1617 namedArgs = self._default_arguments(eval(callableObj,
1617 namedArgs = self._default_arguments(eval(callableObj,
1618 self.namespace))
1618 self.namespace))
1619
1619
1620 # Remove used named arguments from the list, no need to show twice
1620 # Remove used named arguments from the list, no need to show twice
1621 for namedArg in set(namedArgs) - usedNamedArgs:
1621 for namedArg in set(namedArgs) - usedNamedArgs:
1622 if namedArg.startswith(text):
1622 if namedArg.startswith(text):
1623 argMatches.append("%s=" %namedArg)
1623 argMatches.append("%s=" %namedArg)
1624 except:
1624 except:
1625 pass
1625 pass
1626
1626
1627 return argMatches
1627 return argMatches
1628
1628
1629 @staticmethod
1629 @staticmethod
1630 def _get_keys(obj: Any) -> List[Any]:
1630 def _get_keys(obj: Any) -> List[Any]:
1631 # Objects can define their own completions by defining an
1631 # Objects can define their own completions by defining an
1632 # _ipy_key_completions_() method.
1632 # _ipy_key_completions_() method.
1633 method = get_real_method(obj, '_ipython_key_completions_')
1633 method = get_real_method(obj, '_ipython_key_completions_')
1634 if method is not None:
1634 if method is not None:
1635 return method()
1635 return method()
1636
1636
1637 # Special case some common in-memory dict-like types
1637 # Special case some common in-memory dict-like types
1638 if isinstance(obj, dict) or\
1638 if isinstance(obj, dict) or\
1639 _safe_isinstance(obj, 'pandas', 'DataFrame'):
1639 _safe_isinstance(obj, 'pandas', 'DataFrame'):
1640 try:
1640 try:
1641 return list(obj.keys())
1641 return list(obj.keys())
1642 except Exception:
1642 except Exception:
1643 return []
1643 return []
1644 elif _safe_isinstance(obj, 'numpy', 'ndarray') or\
1644 elif _safe_isinstance(obj, 'numpy', 'ndarray') or\
1645 _safe_isinstance(obj, 'numpy', 'void'):
1645 _safe_isinstance(obj, 'numpy', 'void'):
1646 return obj.dtype.names or []
1646 return obj.dtype.names or []
1647 return []
1647 return []
1648
1648
1649 def dict_key_matches(self, text:str) -> List[str]:
1649 def dict_key_matches(self, text:str) -> List[str]:
1650 "Match string keys in a dictionary, after e.g. 'foo[' "
1650 "Match string keys in a dictionary, after e.g. 'foo[' "
1651
1651
1652
1652
1653 if self.__dict_key_regexps is not None:
1653 if self.__dict_key_regexps is not None:
1654 regexps = self.__dict_key_regexps
1654 regexps = self.__dict_key_regexps
1655 else:
1655 else:
1656 dict_key_re_fmt = r'''(?x)
1656 dict_key_re_fmt = r'''(?x)
1657 ( # match dict-referring expression wrt greedy setting
1657 ( # match dict-referring expression wrt greedy setting
1658 %s
1658 %s
1659 )
1659 )
1660 \[ # open bracket
1660 \[ # open bracket
1661 \s* # and optional whitespace
1661 \s* # and optional whitespace
1662 # Capture any number of str-like objects (e.g. "a", "b", 'c')
1662 # Capture any number of str-like objects (e.g. "a", "b", 'c')
1663 ((?:[uUbB]? # string prefix (r not handled)
1663 ((?:[uUbB]? # string prefix (r not handled)
1664 (?:
1664 (?:
1665 '(?:[^']|(?<!\\)\\')*'
1665 '(?:[^']|(?<!\\)\\')*'
1666 |
1666 |
1667 "(?:[^"]|(?<!\\)\\")*"
1667 "(?:[^"]|(?<!\\)\\")*"
1668 )
1668 )
1669 \s*,\s*
1669 \s*,\s*
1670 )*)
1670 )*)
1671 ([uUbB]? # string prefix (r not handled)
1671 ([uUbB]? # string prefix (r not handled)
1672 (?: # unclosed string
1672 (?: # unclosed string
1673 '(?:[^']|(?<!\\)\\')*
1673 '(?:[^']|(?<!\\)\\')*
1674 |
1674 |
1675 "(?:[^"]|(?<!\\)\\")*
1675 "(?:[^"]|(?<!\\)\\")*
1676 )
1676 )
1677 )?
1677 )?
1678 $
1678 $
1679 '''
1679 '''
1680 regexps = self.__dict_key_regexps = {
1680 regexps = self.__dict_key_regexps = {
1681 False: re.compile(dict_key_re_fmt % r'''
1681 False: re.compile(dict_key_re_fmt % r'''
1682 # identifiers separated by .
1682 # identifiers separated by .
1683 (?!\d)\w+
1683 (?!\d)\w+
1684 (?:\.(?!\d)\w+)*
1684 (?:\.(?!\d)\w+)*
1685 '''),
1685 '''),
1686 True: re.compile(dict_key_re_fmt % '''
1686 True: re.compile(dict_key_re_fmt % '''
1687 .+
1687 .+
1688 ''')
1688 ''')
1689 }
1689 }
1690
1690
1691 match = regexps[self.greedy].search(self.text_until_cursor)
1691 match = regexps[self.greedy].search(self.text_until_cursor)
1692
1692
1693 if match is None:
1693 if match is None:
1694 return []
1694 return []
1695
1695
1696 expr, prefix0, prefix = match.groups()
1696 expr, prefix0, prefix = match.groups()
1697 try:
1697 try:
1698 obj = eval(expr, self.namespace)
1698 obj = eval(expr, self.namespace)
1699 except Exception:
1699 except Exception:
1700 try:
1700 try:
1701 obj = eval(expr, self.global_namespace)
1701 obj = eval(expr, self.global_namespace)
1702 except Exception:
1702 except Exception:
1703 return []
1703 return []
1704
1704
1705 keys = self._get_keys(obj)
1705 keys = self._get_keys(obj)
1706 if not keys:
1706 if not keys:
1707 return keys
1707 return keys
1708
1708
1709 extra_prefix = eval(prefix0) if prefix0 != '' else None
1709 extra_prefix = eval(prefix0) if prefix0 != '' else None
1710
1710
1711 closing_quote, token_offset, matches = match_dict_keys(keys, prefix, self.splitter.delims, extra_prefix=extra_prefix)
1711 closing_quote, token_offset, matches = match_dict_keys(keys, prefix, self.splitter.delims, extra_prefix=extra_prefix)
1712 if not matches:
1712 if not matches:
1713 return matches
1713 return matches
1714
1714
1715 # get the cursor position of
1715 # get the cursor position of
1716 # - the text being completed
1716 # - the text being completed
1717 # - the start of the key text
1717 # - the start of the key text
1718 # - the start of the completion
1718 # - the start of the completion
1719 text_start = len(self.text_until_cursor) - len(text)
1719 text_start = len(self.text_until_cursor) - len(text)
1720 if prefix:
1720 if prefix:
1721 key_start = match.start(3)
1721 key_start = match.start(3)
1722 completion_start = key_start + token_offset
1722 completion_start = key_start + token_offset
1723 else:
1723 else:
1724 key_start = completion_start = match.end()
1724 key_start = completion_start = match.end()
1725
1725
1726 # grab the leading prefix, to make sure all completions start with `text`
1726 # grab the leading prefix, to make sure all completions start with `text`
1727 if text_start > key_start:
1727 if text_start > key_start:
1728 leading = ''
1728 leading = ''
1729 else:
1729 else:
1730 leading = text[text_start:completion_start]
1730 leading = text[text_start:completion_start]
1731
1731
1732 # the index of the `[` character
1732 # the index of the `[` character
1733 bracket_idx = match.end(1)
1733 bracket_idx = match.end(1)
1734
1734
1735 # append closing quote and bracket as appropriate
1735 # append closing quote and bracket as appropriate
1736 # this is *not* appropriate if the opening quote or bracket is outside
1736 # this is *not* appropriate if the opening quote or bracket is outside
1737 # the text given to this method
1737 # the text given to this method
1738 suf = ''
1738 suf = ''
1739 continuation = self.line_buffer[len(self.text_until_cursor):]
1739 continuation = self.line_buffer[len(self.text_until_cursor):]
1740 if key_start > text_start and closing_quote:
1740 if key_start > text_start and closing_quote:
1741 # quotes were opened inside text, maybe close them
1741 # quotes were opened inside text, maybe close them
1742 if continuation.startswith(closing_quote):
1742 if continuation.startswith(closing_quote):
1743 continuation = continuation[len(closing_quote):]
1743 continuation = continuation[len(closing_quote):]
1744 else:
1744 else:
1745 suf += closing_quote
1745 suf += closing_quote
1746 if bracket_idx > text_start:
1746 if bracket_idx > text_start:
1747 # brackets were opened inside text, maybe close them
1747 # brackets were opened inside text, maybe close them
1748 if not continuation.startswith(']'):
1748 if not continuation.startswith(']'):
1749 suf += ']'
1749 suf += ']'
1750
1750
1751 return [leading + k + suf for k in matches]
1751 return [leading + k + suf for k in matches]
1752
1752
1753 @staticmethod
1753 @staticmethod
1754 def unicode_name_matches(text:str) -> Tuple[str, List[str]] :
1754 def unicode_name_matches(text:str) -> Tuple[str, List[str]] :
1755 """Match Latex-like syntax for unicode characters base
1755 """Match Latex-like syntax for unicode characters base
1756 on the name of the character.
1756 on the name of the character.
1757
1757
1758 This does ``\\GREEK SMALL LETTER ETA`` -> ``η``
1758 This does ``\\GREEK SMALL LETTER ETA`` -> ``η``
1759
1759
1760 Works only on valid python 3 identifier, or on combining characters that
1760 Works only on valid python 3 identifier, or on combining characters that
1761 will combine to form a valid identifier.
1761 will combine to form a valid identifier.
1762 """
1762 """
1763 slashpos = text.rfind('\\')
1763 slashpos = text.rfind('\\')
1764 if slashpos > -1:
1764 if slashpos > -1:
1765 s = text[slashpos+1:]
1765 s = text[slashpos+1:]
1766 try :
1766 try :
1767 unic = unicodedata.lookup(s)
1767 unic = unicodedata.lookup(s)
1768 # allow combining chars
1768 # allow combining chars
1769 if ('a'+unic).isidentifier():
1769 if ('a'+unic).isidentifier():
1770 return '\\'+s,[unic]
1770 return '\\'+s,[unic]
1771 except KeyError:
1771 except KeyError:
1772 pass
1772 pass
1773 return '', []
1773 return '', []
1774
1774
1775
1775
1776 def latex_matches(self, text:str) -> Tuple[str, Sequence[str]]:
1776 def latex_matches(self, text:str) -> Tuple[str, Sequence[str]]:
1777 """Match Latex syntax for unicode characters.
1777 """Match Latex syntax for unicode characters.
1778
1778
1779 This does both ``\\alp`` -> ``\\alpha`` and ``\\alpha`` -> ``α``
1779 This does both ``\\alp`` -> ``\\alpha`` and ``\\alpha`` -> ``α``
1780 """
1780 """
1781 slashpos = text.rfind('\\')
1781 slashpos = text.rfind('\\')
1782 if slashpos > -1:
1782 if slashpos > -1:
1783 s = text[slashpos:]
1783 s = text[slashpos:]
1784 if s in latex_symbols:
1784 if s in latex_symbols:
1785 # Try to complete a full latex symbol to unicode
1785 # Try to complete a full latex symbol to unicode
1786 # \\alpha -> α
1786 # \\alpha -> α
1787 return s, [latex_symbols[s]]
1787 return s, [latex_symbols[s]]
1788 else:
1788 else:
1789 # If a user has partially typed a latex symbol, give them
1789 # If a user has partially typed a latex symbol, give them
1790 # a full list of options \al -> [\aleph, \alpha]
1790 # a full list of options \al -> [\aleph, \alpha]
1791 matches = [k for k in latex_symbols if k.startswith(s)]
1791 matches = [k for k in latex_symbols if k.startswith(s)]
1792 if matches:
1792 if matches:
1793 return s, matches
1793 return s, matches
1794 return '', ()
1794 return '', ()
1795
1795
1796 def dispatch_custom_completer(self, text):
1796 def dispatch_custom_completer(self, text):
1797 if not self.custom_completers:
1797 if not self.custom_completers:
1798 return
1798 return
1799
1799
1800 line = self.line_buffer
1800 line = self.line_buffer
1801 if not line.strip():
1801 if not line.strip():
1802 return None
1802 return None
1803
1803
1804 # Create a little structure to pass all the relevant information about
1804 # Create a little structure to pass all the relevant information about
1805 # the current completion to any custom completer.
1805 # the current completion to any custom completer.
1806 event = SimpleNamespace()
1806 event = SimpleNamespace()
1807 event.line = line
1807 event.line = line
1808 event.symbol = text
1808 event.symbol = text
1809 cmd = line.split(None,1)[0]
1809 cmd = line.split(None,1)[0]
1810 event.command = cmd
1810 event.command = cmd
1811 event.text_until_cursor = self.text_until_cursor
1811 event.text_until_cursor = self.text_until_cursor
1812
1812
1813 # for foo etc, try also to find completer for %foo
1813 # for foo etc, try also to find completer for %foo
1814 if not cmd.startswith(self.magic_escape):
1814 if not cmd.startswith(self.magic_escape):
1815 try_magic = self.custom_completers.s_matches(
1815 try_magic = self.custom_completers.s_matches(
1816 self.magic_escape + cmd)
1816 self.magic_escape + cmd)
1817 else:
1817 else:
1818 try_magic = []
1818 try_magic = []
1819
1819
1820 for c in itertools.chain(self.custom_completers.s_matches(cmd),
1820 for c in itertools.chain(self.custom_completers.s_matches(cmd),
1821 try_magic,
1821 try_magic,
1822 self.custom_completers.flat_matches(self.text_until_cursor)):
1822 self.custom_completers.flat_matches(self.text_until_cursor)):
1823 try:
1823 try:
1824 res = c(event)
1824 res = c(event)
1825 if res:
1825 if res:
1826 # first, try case sensitive match
1826 # first, try case sensitive match
1827 withcase = [r for r in res if r.startswith(text)]
1827 withcase = [r for r in res if r.startswith(text)]
1828 if withcase:
1828 if withcase:
1829 return withcase
1829 return withcase
1830 # if none, then case insensitive ones are ok too
1830 # if none, then case insensitive ones are ok too
1831 text_low = text.lower()
1831 text_low = text.lower()
1832 return [r for r in res if r.lower().startswith(text_low)]
1832 return [r for r in res if r.lower().startswith(text_low)]
1833 except TryNext:
1833 except TryNext:
1834 pass
1834 pass
1835 except KeyboardInterrupt:
1835 except KeyboardInterrupt:
1836 """
1836 """
1837 If custom completer take too long,
1837 If custom completer take too long,
1838 let keyboard interrupt abort and return nothing.
1838 let keyboard interrupt abort and return nothing.
1839 """
1839 """
1840 break
1840 break
1841
1841
1842 return None
1842 return None
1843
1843
1844 def completions(self, text: str, offset: int)->Iterator[Completion]:
1844 def completions(self, text: str, offset: int)->Iterator[Completion]:
1845 """
1845 """
1846 Returns an iterator over the possible completions
1846 Returns an iterator over the possible completions
1847
1847
1848 .. warning::
1848 .. warning::
1849
1849
1850 Unstable
1850 Unstable
1851
1851
1852 This function is unstable, API may change without warning.
1852 This function is unstable, API may change without warning.
1853 It will also raise unless use in proper context manager.
1853 It will also raise unless use in proper context manager.
1854
1854
1855 Parameters
1855 Parameters
1856 ----------
1856 ----------
1857 text:str
1857 text : str
1858 Full text of the current input, multi line string.
1858 Full text of the current input, multi line string.
1859 offset:int
1859 offset : int
1860 Integer representing the position of the cursor in ``text``. Offset
1860 Integer representing the position of the cursor in ``text``. Offset
1861 is 0-based indexed.
1861 is 0-based indexed.
1862
1862
1863 Yields
1863 Yields
1864 ------
1864 ------
1865 Completion
1865 Completion
1866
1866
1867 Notes
1867 Notes
1868 -----
1868 -----
1869 The cursor on a text can either be seen as being "in between"
1869 The cursor on a text can either be seen as being "in between"
1870 characters or "On" a character depending on the interface visible to
1870 characters or "On" a character depending on the interface visible to
1871 the user. For consistency the cursor being on "in between" characters X
1871 the user. For consistency the cursor being on "in between" characters X
1872 and Y is equivalent to the cursor being "on" character Y, that is to say
1872 and Y is equivalent to the cursor being "on" character Y, that is to say
1873 the character the cursor is on is considered as being after the cursor.
1873 the character the cursor is on is considered as being after the cursor.
1874
1874
1875 Combining characters may span more that one position in the
1875 Combining characters may span more that one position in the
1876 text.
1876 text.
1877
1877
1878 .. note::
1878 .. note::
1879
1879
1880 If ``IPCompleter.debug`` is :any:`True` will yield a ``--jedi/ipython--``
1880 If ``IPCompleter.debug`` is :any:`True` will yield a ``--jedi/ipython--``
1881 fake Completion token to distinguish completion returned by Jedi
1881 fake Completion token to distinguish completion returned by Jedi
1882 and usual IPython completion.
1882 and usual IPython completion.
1883
1883
1884 .. note::
1884 .. note::
1885
1885
1886 Completions are not completely deduplicated yet. If identical
1886 Completions are not completely deduplicated yet. If identical
1887 completions are coming from different sources this function does not
1887 completions are coming from different sources this function does not
1888 ensure that each completion object will only be present once.
1888 ensure that each completion object will only be present once.
1889 """
1889 """
1890 warnings.warn("_complete is a provisional API (as of IPython 6.0). "
1890 warnings.warn("_complete is a provisional API (as of IPython 6.0). "
1891 "It may change without warnings. "
1891 "It may change without warnings. "
1892 "Use in corresponding context manager.",
1892 "Use in corresponding context manager.",
1893 category=ProvisionalCompleterWarning, stacklevel=2)
1893 category=ProvisionalCompleterWarning, stacklevel=2)
1894
1894
1895 seen = set()
1895 seen = set()
1896 profiler:Optional[cProfile.Profile]
1896 profiler:Optional[cProfile.Profile]
1897 try:
1897 try:
1898 if self.profile_completions:
1898 if self.profile_completions:
1899 import cProfile
1899 import cProfile
1900 profiler = cProfile.Profile()
1900 profiler = cProfile.Profile()
1901 profiler.enable()
1901 profiler.enable()
1902 else:
1902 else:
1903 profiler = None
1903 profiler = None
1904
1904
1905 for c in self._completions(text, offset, _timeout=self.jedi_compute_type_timeout/1000):
1905 for c in self._completions(text, offset, _timeout=self.jedi_compute_type_timeout/1000):
1906 if c and (c in seen):
1906 if c and (c in seen):
1907 continue
1907 continue
1908 yield c
1908 yield c
1909 seen.add(c)
1909 seen.add(c)
1910 except KeyboardInterrupt:
1910 except KeyboardInterrupt:
1911 """if completions take too long and users send keyboard interrupt,
1911 """if completions take too long and users send keyboard interrupt,
1912 do not crash and return ASAP. """
1912 do not crash and return ASAP. """
1913 pass
1913 pass
1914 finally:
1914 finally:
1915 if profiler is not None:
1915 if profiler is not None:
1916 profiler.disable()
1916 profiler.disable()
1917 ensure_dir_exists(self.profiler_output_dir)
1917 ensure_dir_exists(self.profiler_output_dir)
1918 output_path = os.path.join(self.profiler_output_dir, str(uuid.uuid4()))
1918 output_path = os.path.join(self.profiler_output_dir, str(uuid.uuid4()))
1919 print("Writing profiler output to", output_path)
1919 print("Writing profiler output to", output_path)
1920 profiler.dump_stats(output_path)
1920 profiler.dump_stats(output_path)
1921
1921
1922 def _completions(self, full_text: str, offset: int, *, _timeout) -> Iterator[Completion]:
1922 def _completions(self, full_text: str, offset: int, *, _timeout) -> Iterator[Completion]:
1923 """
1923 """
1924 Core completion module.Same signature as :any:`completions`, with the
1924 Core completion module.Same signature as :any:`completions`, with the
1925 extra `timeout` parameter (in seconds).
1925 extra `timeout` parameter (in seconds).
1926
1926
1927 Computing jedi's completion ``.type`` can be quite expensive (it is a
1927 Computing jedi's completion ``.type`` can be quite expensive (it is a
1928 lazy property) and can require some warm-up, more warm up than just
1928 lazy property) and can require some warm-up, more warm up than just
1929 computing the ``name`` of a completion. The warm-up can be :
1929 computing the ``name`` of a completion. The warm-up can be :
1930
1930
1931 - Long warm-up the first time a module is encountered after
1931 - Long warm-up the first time a module is encountered after
1932 install/update: actually build parse/inference tree.
1932 install/update: actually build parse/inference tree.
1933
1933
1934 - first time the module is encountered in a session: load tree from
1934 - first time the module is encountered in a session: load tree from
1935 disk.
1935 disk.
1936
1936
1937 We don't want to block completions for tens of seconds so we give the
1937 We don't want to block completions for tens of seconds so we give the
1938 completer a "budget" of ``_timeout`` seconds per invocation to compute
1938 completer a "budget" of ``_timeout`` seconds per invocation to compute
1939 completions types, the completions that have not yet been computed will
1939 completions types, the completions that have not yet been computed will
1940 be marked as "unknown" an will have a chance to be computed next round
1940 be marked as "unknown" an will have a chance to be computed next round
1941 are things get cached.
1941 are things get cached.
1942
1942
1943 Keep in mind that Jedi is not the only thing treating the completion so
1943 Keep in mind that Jedi is not the only thing treating the completion so
1944 keep the timeout short-ish as if we take more than 0.3 second we still
1944 keep the timeout short-ish as if we take more than 0.3 second we still
1945 have lots of processing to do.
1945 have lots of processing to do.
1946
1946
1947 """
1947 """
1948 deadline = time.monotonic() + _timeout
1948 deadline = time.monotonic() + _timeout
1949
1949
1950
1950
1951 before = full_text[:offset]
1951 before = full_text[:offset]
1952 cursor_line, cursor_column = position_to_cursor(full_text, offset)
1952 cursor_line, cursor_column = position_to_cursor(full_text, offset)
1953
1953
1954 matched_text, matches, matches_origin, jedi_matches = self._complete(
1954 matched_text, matches, matches_origin, jedi_matches = self._complete(
1955 full_text=full_text, cursor_line=cursor_line, cursor_pos=cursor_column)
1955 full_text=full_text, cursor_line=cursor_line, cursor_pos=cursor_column)
1956
1956
1957 iter_jm = iter(jedi_matches)
1957 iter_jm = iter(jedi_matches)
1958 if _timeout:
1958 if _timeout:
1959 for jm in iter_jm:
1959 for jm in iter_jm:
1960 try:
1960 try:
1961 type_ = jm.type
1961 type_ = jm.type
1962 except Exception:
1962 except Exception:
1963 if self.debug:
1963 if self.debug:
1964 print("Error in Jedi getting type of ", jm)
1964 print("Error in Jedi getting type of ", jm)
1965 type_ = None
1965 type_ = None
1966 delta = len(jm.name_with_symbols) - len(jm.complete)
1966 delta = len(jm.name_with_symbols) - len(jm.complete)
1967 if type_ == 'function':
1967 if type_ == 'function':
1968 signature = _make_signature(jm)
1968 signature = _make_signature(jm)
1969 else:
1969 else:
1970 signature = ''
1970 signature = ''
1971 yield Completion(start=offset - delta,
1971 yield Completion(start=offset - delta,
1972 end=offset,
1972 end=offset,
1973 text=jm.name_with_symbols,
1973 text=jm.name_with_symbols,
1974 type=type_,
1974 type=type_,
1975 signature=signature,
1975 signature=signature,
1976 _origin='jedi')
1976 _origin='jedi')
1977
1977
1978 if time.monotonic() > deadline:
1978 if time.monotonic() > deadline:
1979 break
1979 break
1980
1980
1981 for jm in iter_jm:
1981 for jm in iter_jm:
1982 delta = len(jm.name_with_symbols) - len(jm.complete)
1982 delta = len(jm.name_with_symbols) - len(jm.complete)
1983 yield Completion(start=offset - delta,
1983 yield Completion(start=offset - delta,
1984 end=offset,
1984 end=offset,
1985 text=jm.name_with_symbols,
1985 text=jm.name_with_symbols,
1986 type='<unknown>', # don't compute type for speed
1986 type='<unknown>', # don't compute type for speed
1987 _origin='jedi',
1987 _origin='jedi',
1988 signature='')
1988 signature='')
1989
1989
1990
1990
1991 start_offset = before.rfind(matched_text)
1991 start_offset = before.rfind(matched_text)
1992
1992
1993 # TODO:
1993 # TODO:
1994 # Suppress this, right now just for debug.
1994 # Suppress this, right now just for debug.
1995 if jedi_matches and matches and self.debug:
1995 if jedi_matches and matches and self.debug:
1996 yield Completion(start=start_offset, end=offset, text='--jedi/ipython--',
1996 yield Completion(start=start_offset, end=offset, text='--jedi/ipython--',
1997 _origin='debug', type='none', signature='')
1997 _origin='debug', type='none', signature='')
1998
1998
1999 # I'm unsure if this is always true, so let's assert and see if it
1999 # I'm unsure if this is always true, so let's assert and see if it
2000 # crash
2000 # crash
2001 assert before.endswith(matched_text)
2001 assert before.endswith(matched_text)
2002 for m, t in zip(matches, matches_origin):
2002 for m, t in zip(matches, matches_origin):
2003 yield Completion(start=start_offset, end=offset, text=m, _origin=t, signature='', type='<unknown>')
2003 yield Completion(start=start_offset, end=offset, text=m, _origin=t, signature='', type='<unknown>')
2004
2004
2005
2005
2006 def complete(self, text=None, line_buffer=None, cursor_pos=None) -> Tuple[str, Sequence[str]]:
2006 def complete(self, text=None, line_buffer=None, cursor_pos=None) -> Tuple[str, Sequence[str]]:
2007 """Find completions for the given text and line context.
2007 """Find completions for the given text and line context.
2008
2008
2009 Note that both the text and the line_buffer are optional, but at least
2009 Note that both the text and the line_buffer are optional, but at least
2010 one of them must be given.
2010 one of them must be given.
2011
2011
2012 Parameters
2012 Parameters
2013 ----------
2013 ----------
2014 text : string, optional
2014 text : string, optional
2015 Text to perform the completion on. If not given, the line buffer
2015 Text to perform the completion on. If not given, the line buffer
2016 is split using the instance's CompletionSplitter object.
2016 is split using the instance's CompletionSplitter object.
2017 line_buffer : string, optional
2017 line_buffer : string, optional
2018 If not given, the completer attempts to obtain the current line
2018 If not given, the completer attempts to obtain the current line
2019 buffer via readline. This keyword allows clients which are
2019 buffer via readline. This keyword allows clients which are
2020 requesting for text completions in non-readline contexts to inform
2020 requesting for text completions in non-readline contexts to inform
2021 the completer of the entire text.
2021 the completer of the entire text.
2022 cursor_pos : int, optional
2022 cursor_pos : int, optional
2023 Index of the cursor in the full line buffer. Should be provided by
2023 Index of the cursor in the full line buffer. Should be provided by
2024 remote frontends where kernel has no access to frontend state.
2024 remote frontends where kernel has no access to frontend state.
2025
2025
2026 Returns
2026 Returns
2027 -------
2027 -------
2028 Tuple of two items:
2028 Tuple of two items:
2029 text : str
2029 text : str
2030 Text that was actually used in the completion.
2030 Text that was actually used in the completion.
2031 matches : list
2031 matches : list
2032 A list of completion matches.
2032 A list of completion matches.
2033
2033
2034 Notes
2034 Notes
2035 -----
2035 -----
2036 This API is likely to be deprecated and replaced by
2036 This API is likely to be deprecated and replaced by
2037 :any:`IPCompleter.completions` in the future.
2037 :any:`IPCompleter.completions` in the future.
2038
2038
2039 """
2039 """
2040 warnings.warn('`Completer.complete` is pending deprecation since '
2040 warnings.warn('`Completer.complete` is pending deprecation since '
2041 'IPython 6.0 and will be replaced by `Completer.completions`.',
2041 'IPython 6.0 and will be replaced by `Completer.completions`.',
2042 PendingDeprecationWarning)
2042 PendingDeprecationWarning)
2043 # potential todo, FOLD the 3rd throw away argument of _complete
2043 # potential todo, FOLD the 3rd throw away argument of _complete
2044 # into the first 2 one.
2044 # into the first 2 one.
2045 return self._complete(line_buffer=line_buffer, cursor_pos=cursor_pos, text=text, cursor_line=0)[:2]
2045 return self._complete(line_buffer=line_buffer, cursor_pos=cursor_pos, text=text, cursor_line=0)[:2]
2046
2046
2047 def _complete(self, *, cursor_line, cursor_pos, line_buffer=None, text=None,
2047 def _complete(self, *, cursor_line, cursor_pos, line_buffer=None, text=None,
2048 full_text=None) -> _CompleteResult:
2048 full_text=None) -> _CompleteResult:
2049 """
2049 """
2050 Like complete but can also returns raw jedi completions as well as the
2050 Like complete but can also returns raw jedi completions as well as the
2051 origin of the completion text. This could (and should) be made much
2051 origin of the completion text. This could (and should) be made much
2052 cleaner but that will be simpler once we drop the old (and stateful)
2052 cleaner but that will be simpler once we drop the old (and stateful)
2053 :any:`complete` API.
2053 :any:`complete` API.
2054
2054
2055 With current provisional API, cursor_pos act both (depending on the
2055 With current provisional API, cursor_pos act both (depending on the
2056 caller) as the offset in the ``text`` or ``line_buffer``, or as the
2056 caller) as the offset in the ``text`` or ``line_buffer``, or as the
2057 ``column`` when passing multiline strings this could/should be renamed
2057 ``column`` when passing multiline strings this could/should be renamed
2058 but would add extra noise.
2058 but would add extra noise.
2059
2059
2060 Returns
2060 Returns
2061 -------
2061 -------
2062 A tuple of N elements which are (likely):
2062 A tuple of N elements which are (likely):
2063 matched_text: ? the text that the complete matched
2063 matched_text: ? the text that the complete matched
2064 matches: list of completions ?
2064 matches: list of completions ?
2065 matches_origin: ? list same lenght as matches, and where each completion came from
2065 matches_origin: ? list same lenght as matches, and where each completion came from
2066 jedi_matches: list of Jedi matches, have it's own structure.
2066 jedi_matches: list of Jedi matches, have it's own structure.
2067 """
2067 """
2068
2068
2069
2069
2070 # if the cursor position isn't given, the only sane assumption we can
2070 # if the cursor position isn't given, the only sane assumption we can
2071 # make is that it's at the end of the line (the common case)
2071 # make is that it's at the end of the line (the common case)
2072 if cursor_pos is None:
2072 if cursor_pos is None:
2073 cursor_pos = len(line_buffer) if text is None else len(text)
2073 cursor_pos = len(line_buffer) if text is None else len(text)
2074
2074
2075 if self.use_main_ns:
2075 if self.use_main_ns:
2076 self.namespace = __main__.__dict__
2076 self.namespace = __main__.__dict__
2077
2077
2078 # if text is either None or an empty string, rely on the line buffer
2078 # if text is either None or an empty string, rely on the line buffer
2079 if (not line_buffer) and full_text:
2079 if (not line_buffer) and full_text:
2080 line_buffer = full_text.split('\n')[cursor_line]
2080 line_buffer = full_text.split('\n')[cursor_line]
2081 if not text: # issue #11508: check line_buffer before calling split_line
2081 if not text: # issue #11508: check line_buffer before calling split_line
2082 text = self.splitter.split_line(line_buffer, cursor_pos) if line_buffer else ''
2082 text = self.splitter.split_line(line_buffer, cursor_pos) if line_buffer else ''
2083
2083
2084 if self.backslash_combining_completions:
2084 if self.backslash_combining_completions:
2085 # allow deactivation of these on windows.
2085 # allow deactivation of these on windows.
2086 base_text = text if not line_buffer else line_buffer[:cursor_pos]
2086 base_text = text if not line_buffer else line_buffer[:cursor_pos]
2087
2087
2088 for meth in (self.latex_matches,
2088 for meth in (self.latex_matches,
2089 self.unicode_name_matches,
2089 self.unicode_name_matches,
2090 back_latex_name_matches,
2090 back_latex_name_matches,
2091 back_unicode_name_matches,
2091 back_unicode_name_matches,
2092 self.fwd_unicode_match):
2092 self.fwd_unicode_match):
2093 name_text, name_matches = meth(base_text)
2093 name_text, name_matches = meth(base_text)
2094 if name_text:
2094 if name_text:
2095 return _CompleteResult(name_text, name_matches[:MATCHES_LIMIT], \
2095 return _CompleteResult(name_text, name_matches[:MATCHES_LIMIT], \
2096 [meth.__qualname__]*min(len(name_matches), MATCHES_LIMIT), ())
2096 [meth.__qualname__]*min(len(name_matches), MATCHES_LIMIT), ())
2097
2097
2098
2098
2099 # If no line buffer is given, assume the input text is all there was
2099 # If no line buffer is given, assume the input text is all there was
2100 if line_buffer is None:
2100 if line_buffer is None:
2101 line_buffer = text
2101 line_buffer = text
2102
2102
2103 self.line_buffer = line_buffer
2103 self.line_buffer = line_buffer
2104 self.text_until_cursor = self.line_buffer[:cursor_pos]
2104 self.text_until_cursor = self.line_buffer[:cursor_pos]
2105
2105
2106 # Do magic arg matches
2106 # Do magic arg matches
2107 for matcher in self.magic_arg_matchers:
2107 for matcher in self.magic_arg_matchers:
2108 matches = list(matcher(line_buffer))[:MATCHES_LIMIT]
2108 matches = list(matcher(line_buffer))[:MATCHES_LIMIT]
2109 if matches:
2109 if matches:
2110 origins = [matcher.__qualname__] * len(matches)
2110 origins = [matcher.__qualname__] * len(matches)
2111 return _CompleteResult(text, matches, origins, ())
2111 return _CompleteResult(text, matches, origins, ())
2112
2112
2113 # Start with a clean slate of completions
2113 # Start with a clean slate of completions
2114 matches = []
2114 matches = []
2115
2115
2116 # FIXME: we should extend our api to return a dict with completions for
2116 # FIXME: we should extend our api to return a dict with completions for
2117 # different types of objects. The rlcomplete() method could then
2117 # different types of objects. The rlcomplete() method could then
2118 # simply collapse the dict into a list for readline, but we'd have
2118 # simply collapse the dict into a list for readline, but we'd have
2119 # richer completion semantics in other environments.
2119 # richer completion semantics in other environments.
2120 completions:Iterable[Any] = []
2120 completions:Iterable[Any] = []
2121 if self.use_jedi:
2121 if self.use_jedi:
2122 if not full_text:
2122 if not full_text:
2123 full_text = line_buffer
2123 full_text = line_buffer
2124 completions = self._jedi_matches(
2124 completions = self._jedi_matches(
2125 cursor_pos, cursor_line, full_text)
2125 cursor_pos, cursor_line, full_text)
2126
2126
2127 if self.merge_completions:
2127 if self.merge_completions:
2128 matches = []
2128 matches = []
2129 for matcher in self.matchers:
2129 for matcher in self.matchers:
2130 try:
2130 try:
2131 matches.extend([(m, matcher.__qualname__)
2131 matches.extend([(m, matcher.__qualname__)
2132 for m in matcher(text)])
2132 for m in matcher(text)])
2133 except:
2133 except:
2134 # Show the ugly traceback if the matcher causes an
2134 # Show the ugly traceback if the matcher causes an
2135 # exception, but do NOT crash the kernel!
2135 # exception, but do NOT crash the kernel!
2136 sys.excepthook(*sys.exc_info())
2136 sys.excepthook(*sys.exc_info())
2137 else:
2137 else:
2138 for matcher in self.matchers:
2138 for matcher in self.matchers:
2139 matches = [(m, matcher.__qualname__)
2139 matches = [(m, matcher.__qualname__)
2140 for m in matcher(text)]
2140 for m in matcher(text)]
2141 if matches:
2141 if matches:
2142 break
2142 break
2143
2143
2144 seen = set()
2144 seen = set()
2145 filtered_matches = set()
2145 filtered_matches = set()
2146 for m in matches:
2146 for m in matches:
2147 t, c = m
2147 t, c = m
2148 if t not in seen:
2148 if t not in seen:
2149 filtered_matches.add(m)
2149 filtered_matches.add(m)
2150 seen.add(t)
2150 seen.add(t)
2151
2151
2152 _filtered_matches = sorted(filtered_matches, key=lambda x: completions_sorting_key(x[0]))
2152 _filtered_matches = sorted(filtered_matches, key=lambda x: completions_sorting_key(x[0]))
2153
2153
2154 custom_res = [(m, 'custom') for m in self.dispatch_custom_completer(text) or []]
2154 custom_res = [(m, 'custom') for m in self.dispatch_custom_completer(text) or []]
2155
2155
2156 _filtered_matches = custom_res or _filtered_matches
2156 _filtered_matches = custom_res or _filtered_matches
2157
2157
2158 _filtered_matches = _filtered_matches[:MATCHES_LIMIT]
2158 _filtered_matches = _filtered_matches[:MATCHES_LIMIT]
2159 _matches = [m[0] for m in _filtered_matches]
2159 _matches = [m[0] for m in _filtered_matches]
2160 origins = [m[1] for m in _filtered_matches]
2160 origins = [m[1] for m in _filtered_matches]
2161
2161
2162 self.matches = _matches
2162 self.matches = _matches
2163
2163
2164 return _CompleteResult(text, _matches, origins, completions)
2164 return _CompleteResult(text, _matches, origins, completions)
2165
2165
2166 def fwd_unicode_match(self, text:str) -> Tuple[str, Sequence[str]]:
2166 def fwd_unicode_match(self, text:str) -> Tuple[str, Sequence[str]]:
2167 """
2167 """
2168 Forward match a string starting with a backslash with a list of
2168 Forward match a string starting with a backslash with a list of
2169 potential Unicode completions.
2169 potential Unicode completions.
2170
2170
2171 Will compute list list of Unicode character names on first call and cache it.
2171 Will compute list list of Unicode character names on first call and cache it.
2172
2172
2173 Returns
2173 Returns
2174 -------
2174 -------
2175 At tuple with:
2175 At tuple with:
2176 - matched text (empty if no matches)
2176 - matched text (empty if no matches)
2177 - list of potential completions, empty tuple otherwise)
2177 - list of potential completions, empty tuple otherwise)
2178 """
2178 """
2179 # TODO: self.unicode_names is here a list we traverse each time with ~100k elements.
2179 # TODO: self.unicode_names is here a list we traverse each time with ~100k elements.
2180 # We could do a faster match using a Trie.
2180 # We could do a faster match using a Trie.
2181
2181
2182 # Using pygtrie the follwing seem to work:
2182 # Using pygtrie the follwing seem to work:
2183
2183
2184 # s = PrefixSet()
2184 # s = PrefixSet()
2185
2185
2186 # for c in range(0,0x10FFFF + 1):
2186 # for c in range(0,0x10FFFF + 1):
2187 # try:
2187 # try:
2188 # s.add(unicodedata.name(chr(c)))
2188 # s.add(unicodedata.name(chr(c)))
2189 # except ValueError:
2189 # except ValueError:
2190 # pass
2190 # pass
2191 # [''.join(k) for k in s.iter(prefix)]
2191 # [''.join(k) for k in s.iter(prefix)]
2192
2192
2193 # But need to be timed and adds an extra dependency.
2193 # But need to be timed and adds an extra dependency.
2194
2194
2195 slashpos = text.rfind('\\')
2195 slashpos = text.rfind('\\')
2196 # if text starts with slash
2196 # if text starts with slash
2197 if slashpos > -1:
2197 if slashpos > -1:
2198 # PERF: It's important that we don't access self._unicode_names
2198 # PERF: It's important that we don't access self._unicode_names
2199 # until we're inside this if-block. _unicode_names is lazily
2199 # until we're inside this if-block. _unicode_names is lazily
2200 # initialized, and it takes a user-noticeable amount of time to
2200 # initialized, and it takes a user-noticeable amount of time to
2201 # initialize it, so we don't want to initialize it unless we're
2201 # initialize it, so we don't want to initialize it unless we're
2202 # actually going to use it.
2202 # actually going to use it.
2203 s = text[slashpos+1:]
2203 s = text[slashpos+1:]
2204 candidates = [x for x in self.unicode_names if x.startswith(s)]
2204 candidates = [x for x in self.unicode_names if x.startswith(s)]
2205 if candidates:
2205 if candidates:
2206 return s, candidates
2206 return s, candidates
2207 else:
2207 else:
2208 return '', ()
2208 return '', ()
2209
2209
2210 # if text does not start with slash
2210 # if text does not start with slash
2211 else:
2211 else:
2212 return '', ()
2212 return '', ()
2213
2213
2214 @property
2214 @property
2215 def unicode_names(self) -> List[str]:
2215 def unicode_names(self) -> List[str]:
2216 """List of names of unicode code points that can be completed.
2216 """List of names of unicode code points that can be completed.
2217
2217
2218 The list is lazily initialized on first access.
2218 The list is lazily initialized on first access.
2219 """
2219 """
2220 if self._unicode_names is None:
2220 if self._unicode_names is None:
2221 names = []
2221 names = []
2222 for c in range(0,0x10FFFF + 1):
2222 for c in range(0,0x10FFFF + 1):
2223 try:
2223 try:
2224 names.append(unicodedata.name(chr(c)))
2224 names.append(unicodedata.name(chr(c)))
2225 except ValueError:
2225 except ValueError:
2226 pass
2226 pass
2227 self._unicode_names = _unicode_name_compute(_UNICODE_RANGES)
2227 self._unicode_names = _unicode_name_compute(_UNICODE_RANGES)
2228
2228
2229 return self._unicode_names
2229 return self._unicode_names
2230
2230
2231 def _unicode_name_compute(ranges:List[Tuple[int,int]]) -> List[str]:
2231 def _unicode_name_compute(ranges:List[Tuple[int,int]]) -> List[str]:
2232 names = []
2232 names = []
2233 for start,stop in ranges:
2233 for start,stop in ranges:
2234 for c in range(start, stop) :
2234 for c in range(start, stop) :
2235 try:
2235 try:
2236 names.append(unicodedata.name(chr(c)))
2236 names.append(unicodedata.name(chr(c)))
2237 except ValueError:
2237 except ValueError:
2238 pass
2238 pass
2239 return names
2239 return names
@@ -1,354 +1,370 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """Implementations for various useful completers.
2 """Implementations for various useful completers.
3
3
4 These are all loaded by default by IPython.
4 These are all loaded by default by IPython.
5 """
5 """
6 #-----------------------------------------------------------------------------
6 #-----------------------------------------------------------------------------
7 # Copyright (C) 2010-2011 The IPython Development Team.
7 # Copyright (C) 2010-2011 The IPython Development Team.
8 #
8 #
9 # Distributed under the terms of the BSD License.
9 # Distributed under the terms of the BSD License.
10 #
10 #
11 # The full license is in the file COPYING.txt, distributed with this software.
11 # The full license is in the file COPYING.txt, distributed with this software.
12 #-----------------------------------------------------------------------------
12 #-----------------------------------------------------------------------------
13
13
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 # Imports
15 # Imports
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 # Stdlib imports
18 # Stdlib imports
19 import glob
19 import glob
20 import inspect
20 import inspect
21 import os
21 import os
22 import re
22 import re
23 import sys
23 import sys
24 from importlib import import_module
24 from importlib import import_module
25 from importlib.machinery import all_suffixes
25 from importlib.machinery import all_suffixes
26
26
27
27
28 # Third-party imports
28 # Third-party imports
29 from time import time
29 from time import time
30 from zipimport import zipimporter
30 from zipimport import zipimporter
31
31
32 # Our own imports
32 # Our own imports
33 from .completer import expand_user, compress_user
33 from .completer import expand_user, compress_user
34 from .error import TryNext
34 from .error import TryNext
35 from ..utils._process_common import arg_split
35 from ..utils._process_common import arg_split
36
36
37 # FIXME: this should be pulled in with the right call via the component system
37 # FIXME: this should be pulled in with the right call via the component system
38 from IPython import get_ipython
38 from IPython import get_ipython
39
39
40 from typing import List
40 from typing import List
41
41
42 #-----------------------------------------------------------------------------
42 #-----------------------------------------------------------------------------
43 # Globals and constants
43 # Globals and constants
44 #-----------------------------------------------------------------------------
44 #-----------------------------------------------------------------------------
45 _suffixes = all_suffixes()
45 _suffixes = all_suffixes()
46
46
47 # Time in seconds after which the rootmodules will be stored permanently in the
47 # Time in seconds after which the rootmodules will be stored permanently in the
48 # ipython ip.db database (kept in the user's .ipython dir).
48 # ipython ip.db database (kept in the user's .ipython dir).
49 TIMEOUT_STORAGE = 2
49 TIMEOUT_STORAGE = 2
50
50
51 # Time in seconds after which we give up
51 # Time in seconds after which we give up
52 TIMEOUT_GIVEUP = 20
52 TIMEOUT_GIVEUP = 20
53
53
54 # Regular expression for the python import statement
54 # Regular expression for the python import statement
55 import_re = re.compile(r'(?P<name>[^\W\d]\w*?)'
55 import_re = re.compile(r'(?P<name>[^\W\d]\w*?)'
56 r'(?P<package>[/\\]__init__)?'
56 r'(?P<package>[/\\]__init__)?'
57 r'(?P<suffix>%s)$' %
57 r'(?P<suffix>%s)$' %
58 r'|'.join(re.escape(s) for s in _suffixes))
58 r'|'.join(re.escape(s) for s in _suffixes))
59
59
60 # RE for the ipython %run command (python + ipython scripts)
60 # RE for the ipython %run command (python + ipython scripts)
61 magic_run_re = re.compile(r'.*(\.ipy|\.ipynb|\.py[w]?)$')
61 magic_run_re = re.compile(r'.*(\.ipy|\.ipynb|\.py[w]?)$')
62
62
63 #-----------------------------------------------------------------------------
63 #-----------------------------------------------------------------------------
64 # Local utilities
64 # Local utilities
65 #-----------------------------------------------------------------------------
65 #-----------------------------------------------------------------------------
66
66
67 def module_list(path):
67 def module_list(path):
68 """
68 """
69 Return the list containing the names of the modules available in the given
69 Return the list containing the names of the modules available in the given
70 folder.
70 folder.
71 """
71 """
72 # sys.path has the cwd as an empty string, but isdir/listdir need it as '.'
72 # sys.path has the cwd as an empty string, but isdir/listdir need it as '.'
73 if path == '':
73 if path == '':
74 path = '.'
74 path = '.'
75
75
76 # A few local constants to be used in loops below
76 # A few local constants to be used in loops below
77 pjoin = os.path.join
77 pjoin = os.path.join
78
78
79 if os.path.isdir(path):
79 if os.path.isdir(path):
80 # Build a list of all files in the directory and all files
80 # Build a list of all files in the directory and all files
81 # in its subdirectories. For performance reasons, do not
81 # in its subdirectories. For performance reasons, do not
82 # recurse more than one level into subdirectories.
82 # recurse more than one level into subdirectories.
83 files = []
83 files = []
84 for root, dirs, nondirs in os.walk(path, followlinks=True):
84 for root, dirs, nondirs in os.walk(path, followlinks=True):
85 subdir = root[len(path)+1:]
85 subdir = root[len(path)+1:]
86 if subdir:
86 if subdir:
87 files.extend(pjoin(subdir, f) for f in nondirs)
87 files.extend(pjoin(subdir, f) for f in nondirs)
88 dirs[:] = [] # Do not recurse into additional subdirectories.
88 dirs[:] = [] # Do not recurse into additional subdirectories.
89 else:
89 else:
90 files.extend(nondirs)
90 files.extend(nondirs)
91
91
92 else:
92 else:
93 try:
93 try:
94 files = list(zipimporter(path)._files.keys())
94 files = list(zipimporter(path)._files.keys())
95 except:
95 except:
96 files = []
96 files = []
97
97
98 # Build a list of modules which match the import_re regex.
98 # Build a list of modules which match the import_re regex.
99 modules = []
99 modules = []
100 for f in files:
100 for f in files:
101 m = import_re.match(f)
101 m = import_re.match(f)
102 if m:
102 if m:
103 modules.append(m.group('name'))
103 modules.append(m.group('name'))
104 return list(set(modules))
104 return list(set(modules))
105
105
106
106
107 def get_root_modules():
107 def get_root_modules():
108 """
108 """
109 Returns a list containing the names of all the modules available in the
109 Returns a list containing the names of all the modules available in the
110 folders of the pythonpath.
110 folders of the pythonpath.
111
111
112 ip.db['rootmodules_cache'] maps sys.path entries to list of modules.
112 ip.db['rootmodules_cache'] maps sys.path entries to list of modules.
113 """
113 """
114 ip = get_ipython()
114 ip = get_ipython()
115 if ip is None:
115 if ip is None:
116 # No global shell instance to store cached list of modules.
116 # No global shell instance to store cached list of modules.
117 # Don't try to scan for modules every time.
117 # Don't try to scan for modules every time.
118 return list(sys.builtin_module_names)
118 return list(sys.builtin_module_names)
119
119
120 rootmodules_cache = ip.db.get('rootmodules_cache', {})
120 rootmodules_cache = ip.db.get('rootmodules_cache', {})
121 rootmodules = list(sys.builtin_module_names)
121 rootmodules = list(sys.builtin_module_names)
122 start_time = time()
122 start_time = time()
123 store = False
123 store = False
124 for path in sys.path:
124 for path in sys.path:
125 try:
125 try:
126 modules = rootmodules_cache[path]
126 modules = rootmodules_cache[path]
127 except KeyError:
127 except KeyError:
128 modules = module_list(path)
128 modules = module_list(path)
129 try:
129 try:
130 modules.remove('__init__')
130 modules.remove('__init__')
131 except ValueError:
131 except ValueError:
132 pass
132 pass
133 if path not in ('', '.'): # cwd modules should not be cached
133 if path not in ('', '.'): # cwd modules should not be cached
134 rootmodules_cache[path] = modules
134 rootmodules_cache[path] = modules
135 if time() - start_time > TIMEOUT_STORAGE and not store:
135 if time() - start_time > TIMEOUT_STORAGE and not store:
136 store = True
136 store = True
137 print("\nCaching the list of root modules, please wait!")
137 print("\nCaching the list of root modules, please wait!")
138 print("(This will only be done once - type '%rehashx' to "
138 print("(This will only be done once - type '%rehashx' to "
139 "reset cache!)\n")
139 "reset cache!)\n")
140 sys.stdout.flush()
140 sys.stdout.flush()
141 if time() - start_time > TIMEOUT_GIVEUP:
141 if time() - start_time > TIMEOUT_GIVEUP:
142 print("This is taking too long, we give up.\n")
142 print("This is taking too long, we give up.\n")
143 return []
143 return []
144 rootmodules.extend(modules)
144 rootmodules.extend(modules)
145 if store:
145 if store:
146 ip.db['rootmodules_cache'] = rootmodules_cache
146 ip.db['rootmodules_cache'] = rootmodules_cache
147 rootmodules = list(set(rootmodules))
147 rootmodules = list(set(rootmodules))
148 return rootmodules
148 return rootmodules
149
149
150
150
151 def is_importable(module, attr, only_modules):
151 def is_importable(module, attr, only_modules):
152 if only_modules:
152 if only_modules:
153 return inspect.ismodule(getattr(module, attr))
153 return inspect.ismodule(getattr(module, attr))
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 """
160 Try to import given module and return list of potential completions.
171 Try to import given module and return list of potential completions.
161 """
172 """
162 mod = mod.rstrip('.')
173 mod = mod.rstrip('.')
163 try:
174 try:
164 m = import_module(mod)
175 m = import_module(mod)
165 except:
176 except:
166 return []
177 return []
167
178
168 m_is_init = '__init__' in (getattr(m, '__file__', '') or '')
179 m_is_init = '__init__' in (getattr(m, '__file__', '') or '')
169
180
170 completions = []
181 completions = []
171 if (not hasattr(m, '__file__')) or (not only_modules) or m_is_init:
182 if (not hasattr(m, '__file__')) or (not only_modules) or m_is_init:
172 completions.extend( [attr for attr in dir(m) if
183 completions.extend( [attr for attr in dir(m) if
173 is_importable(m, attr, only_modules)])
184 is_importable(m, attr, only_modules)])
174
185
175 completions.extend(getattr(m, '__all__', []))
186 m_all = getattr(m, "__all__", [])
187 if only_modules:
188 completions.extend(attr for attr in m_all if is_possible_submodule(m, attr))
189 else:
190 completions.extend(m_all)
191
176 if m_is_init:
192 if m_is_init:
177 completions.extend(module_list(os.path.dirname(m.__file__)))
193 completions.extend(module_list(os.path.dirname(m.__file__)))
178 completions_set = {c for c in completions if isinstance(c, str)}
194 completions_set = {c for c in completions if isinstance(c, str)}
179 completions_set.discard('__init__')
195 completions_set.discard('__init__')
180 return list(completions_set)
196 return list(completions_set)
181
197
182
198
183 #-----------------------------------------------------------------------------
199 #-----------------------------------------------------------------------------
184 # Completion-related functions.
200 # Completion-related functions.
185 #-----------------------------------------------------------------------------
201 #-----------------------------------------------------------------------------
186
202
187 def quick_completer(cmd, completions):
203 def quick_completer(cmd, completions):
188 r""" Easily create a trivial completer for a command.
204 r""" Easily create a trivial completer for a command.
189
205
190 Takes either a list of completions, or all completions in string (that will
206 Takes either a list of completions, or all completions in string (that will
191 be split on whitespace).
207 be split on whitespace).
192
208
193 Example::
209 Example::
194
210
195 [d:\ipython]|1> import ipy_completers
211 [d:\ipython]|1> import ipy_completers
196 [d:\ipython]|2> ipy_completers.quick_completer('foo', ['bar','baz'])
212 [d:\ipython]|2> ipy_completers.quick_completer('foo', ['bar','baz'])
197 [d:\ipython]|3> foo b<TAB>
213 [d:\ipython]|3> foo b<TAB>
198 bar baz
214 bar baz
199 [d:\ipython]|3> foo ba
215 [d:\ipython]|3> foo ba
200 """
216 """
201
217
202 if isinstance(completions, str):
218 if isinstance(completions, str):
203 completions = completions.split()
219 completions = completions.split()
204
220
205 def do_complete(self, event):
221 def do_complete(self, event):
206 return completions
222 return completions
207
223
208 get_ipython().set_hook('complete_command',do_complete, str_key = cmd)
224 get_ipython().set_hook('complete_command',do_complete, str_key = cmd)
209
225
210 def module_completion(line):
226 def module_completion(line):
211 """
227 """
212 Returns a list containing the completion possibilities for an import line.
228 Returns a list containing the completion possibilities for an import line.
213
229
214 The line looks like this :
230 The line looks like this :
215 'import xml.d'
231 'import xml.d'
216 'from xml.dom import'
232 'from xml.dom import'
217 """
233 """
218
234
219 words = line.split(' ')
235 words = line.split(' ')
220 nwords = len(words)
236 nwords = len(words)
221
237
222 # from whatever <tab> -> 'import '
238 # from whatever <tab> -> 'import '
223 if nwords == 3 and words[0] == 'from':
239 if nwords == 3 and words[0] == 'from':
224 return ['import ']
240 return ['import ']
225
241
226 # 'from xy<tab>' or 'import xy<tab>'
242 # 'from xy<tab>' or 'import xy<tab>'
227 if nwords < 3 and (words[0] in {'%aimport', 'import', 'from'}) :
243 if nwords < 3 and (words[0] in {'%aimport', 'import', 'from'}) :
228 if nwords == 1:
244 if nwords == 1:
229 return get_root_modules()
245 return get_root_modules()
230 mod = words[1].split('.')
246 mod = words[1].split('.')
231 if len(mod) < 2:
247 if len(mod) < 2:
232 return get_root_modules()
248 return get_root_modules()
233 completion_list = try_import('.'.join(mod[:-1]), True)
249 completion_list = try_import('.'.join(mod[:-1]), True)
234 return ['.'.join(mod[:-1] + [el]) for el in completion_list]
250 return ['.'.join(mod[:-1] + [el]) for el in completion_list]
235
251
236 # 'from xyz import abc<tab>'
252 # 'from xyz import abc<tab>'
237 if nwords >= 3 and words[0] == 'from':
253 if nwords >= 3 and words[0] == 'from':
238 mod = words[1]
254 mod = words[1]
239 return try_import(mod)
255 return try_import(mod)
240
256
241 #-----------------------------------------------------------------------------
257 #-----------------------------------------------------------------------------
242 # Completers
258 # Completers
243 #-----------------------------------------------------------------------------
259 #-----------------------------------------------------------------------------
244 # These all have the func(self, event) signature to be used as custom
260 # These all have the func(self, event) signature to be used as custom
245 # completers
261 # completers
246
262
247 def module_completer(self,event):
263 def module_completer(self,event):
248 """Give completions after user has typed 'import ...' or 'from ...'"""
264 """Give completions after user has typed 'import ...' or 'from ...'"""
249
265
250 # This works in all versions of python. While 2.5 has
266 # This works in all versions of python. While 2.5 has
251 # pkgutil.walk_packages(), that particular routine is fairly dangerous,
267 # pkgutil.walk_packages(), that particular routine is fairly dangerous,
252 # since it imports *EVERYTHING* on sys.path. That is: a) very slow b) full
268 # since it imports *EVERYTHING* on sys.path. That is: a) very slow b) full
253 # of possibly problematic side effects.
269 # of possibly problematic side effects.
254 # This search the folders in the sys.path for available modules.
270 # This search the folders in the sys.path for available modules.
255
271
256 return module_completion(event.line)
272 return module_completion(event.line)
257
273
258 # FIXME: there's a lot of logic common to the run, cd and builtin file
274 # FIXME: there's a lot of logic common to the run, cd and builtin file
259 # completers, that is currently reimplemented in each.
275 # completers, that is currently reimplemented in each.
260
276
261 def magic_run_completer(self, event):
277 def magic_run_completer(self, event):
262 """Complete files that end in .py or .ipy or .ipynb for the %run command.
278 """Complete files that end in .py or .ipy or .ipynb for the %run command.
263 """
279 """
264 comps = arg_split(event.line, strict=False)
280 comps = arg_split(event.line, strict=False)
265 # relpath should be the current token that we need to complete.
281 # relpath should be the current token that we need to complete.
266 if (len(comps) > 1) and (not event.line.endswith(' ')):
282 if (len(comps) > 1) and (not event.line.endswith(' ')):
267 relpath = comps[-1].strip("'\"")
283 relpath = comps[-1].strip("'\"")
268 else:
284 else:
269 relpath = ''
285 relpath = ''
270
286
271 #print("\nev=", event) # dbg
287 #print("\nev=", event) # dbg
272 #print("rp=", relpath) # dbg
288 #print("rp=", relpath) # dbg
273 #print('comps=', comps) # dbg
289 #print('comps=', comps) # dbg
274
290
275 lglob = glob.glob
291 lglob = glob.glob
276 isdir = os.path.isdir
292 isdir = os.path.isdir
277 relpath, tilde_expand, tilde_val = expand_user(relpath)
293 relpath, tilde_expand, tilde_val = expand_user(relpath)
278
294
279 # Find if the user has already typed the first filename, after which we
295 # Find if the user has already typed the first filename, after which we
280 # should complete on all files, since after the first one other files may
296 # should complete on all files, since after the first one other files may
281 # be arguments to the input script.
297 # be arguments to the input script.
282
298
283 if any(magic_run_re.match(c) for c in comps):
299 if any(magic_run_re.match(c) for c in comps):
284 matches = [f.replace('\\','/') + ('/' if isdir(f) else '')
300 matches = [f.replace('\\','/') + ('/' if isdir(f) else '')
285 for f in lglob(relpath+'*')]
301 for f in lglob(relpath+'*')]
286 else:
302 else:
287 dirs = [f.replace('\\','/') + "/" for f in lglob(relpath+'*') if isdir(f)]
303 dirs = [f.replace('\\','/') + "/" for f in lglob(relpath+'*') if isdir(f)]
288 pys = [f.replace('\\','/')
304 pys = [f.replace('\\','/')
289 for f in lglob(relpath+'*.py') + lglob(relpath+'*.ipy') +
305 for f in lglob(relpath+'*.py') + lglob(relpath+'*.ipy') +
290 lglob(relpath+'*.ipynb') + lglob(relpath + '*.pyw')]
306 lglob(relpath+'*.ipynb') + lglob(relpath + '*.pyw')]
291
307
292 matches = dirs + pys
308 matches = dirs + pys
293
309
294 #print('run comp:', dirs+pys) # dbg
310 #print('run comp:', dirs+pys) # dbg
295 return [compress_user(p, tilde_expand, tilde_val) for p in matches]
311 return [compress_user(p, tilde_expand, tilde_val) for p in matches]
296
312
297
313
298 def cd_completer(self, event):
314 def cd_completer(self, event):
299 """Completer function for cd, which only returns directories."""
315 """Completer function for cd, which only returns directories."""
300 ip = get_ipython()
316 ip = get_ipython()
301 relpath = event.symbol
317 relpath = event.symbol
302
318
303 #print(event) # dbg
319 #print(event) # dbg
304 if event.line.endswith('-b') or ' -b ' in event.line:
320 if event.line.endswith('-b') or ' -b ' in event.line:
305 # return only bookmark completions
321 # return only bookmark completions
306 bkms = self.db.get('bookmarks', None)
322 bkms = self.db.get('bookmarks', None)
307 if bkms:
323 if bkms:
308 return bkms.keys()
324 return bkms.keys()
309 else:
325 else:
310 return []
326 return []
311
327
312 if event.symbol == '-':
328 if event.symbol == '-':
313 width_dh = str(len(str(len(ip.user_ns['_dh']) + 1)))
329 width_dh = str(len(str(len(ip.user_ns['_dh']) + 1)))
314 # jump in directory history by number
330 # jump in directory history by number
315 fmt = '-%0' + width_dh +'d [%s]'
331 fmt = '-%0' + width_dh +'d [%s]'
316 ents = [ fmt % (i,s) for i,s in enumerate(ip.user_ns['_dh'])]
332 ents = [ fmt % (i,s) for i,s in enumerate(ip.user_ns['_dh'])]
317 if len(ents) > 1:
333 if len(ents) > 1:
318 return ents
334 return ents
319 return []
335 return []
320
336
321 if event.symbol.startswith('--'):
337 if event.symbol.startswith('--'):
322 return ["--" + os.path.basename(d) for d in ip.user_ns['_dh']]
338 return ["--" + os.path.basename(d) for d in ip.user_ns['_dh']]
323
339
324 # Expand ~ in path and normalize directory separators.
340 # Expand ~ in path and normalize directory separators.
325 relpath, tilde_expand, tilde_val = expand_user(relpath)
341 relpath, tilde_expand, tilde_val = expand_user(relpath)
326 relpath = relpath.replace('\\','/')
342 relpath = relpath.replace('\\','/')
327
343
328 found = []
344 found = []
329 for d in [f.replace('\\','/') + '/' for f in glob.glob(relpath+'*')
345 for d in [f.replace('\\','/') + '/' for f in glob.glob(relpath+'*')
330 if os.path.isdir(f)]:
346 if os.path.isdir(f)]:
331 if ' ' in d:
347 if ' ' in d:
332 # we don't want to deal with any of that, complex code
348 # we don't want to deal with any of that, complex code
333 # for this is elsewhere
349 # for this is elsewhere
334 raise TryNext
350 raise TryNext
335
351
336 found.append(d)
352 found.append(d)
337
353
338 if not found:
354 if not found:
339 if os.path.isdir(relpath):
355 if os.path.isdir(relpath):
340 return [compress_user(relpath, tilde_expand, tilde_val)]
356 return [compress_user(relpath, tilde_expand, tilde_val)]
341
357
342 # if no completions so far, try bookmarks
358 # if no completions so far, try bookmarks
343 bks = self.db.get('bookmarks',{})
359 bks = self.db.get('bookmarks',{})
344 bkmatches = [s for s in bks if s.startswith(event.symbol)]
360 bkmatches = [s for s in bks if s.startswith(event.symbol)]
345 if bkmatches:
361 if bkmatches:
346 return bkmatches
362 return bkmatches
347
363
348 raise TryNext
364 raise TryNext
349
365
350 return [compress_user(p, tilde_expand, tilde_val) for p in found]
366 return [compress_user(p, tilde_expand, tilde_val) for p in found]
351
367
352 def reset_completer(self, event):
368 def reset_completer(self, event):
353 "A completer for %reset magic"
369 "A completer for %reset magic"
354 return '-f -s in out array dhist'.split()
370 return '-f -s in out array dhist'.split()
@@ -1,229 +1,223 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """sys.excepthook for IPython itself, leaves a detailed report on disk.
2 """sys.excepthook for IPython itself, leaves a detailed report on disk.
3
3
4 Authors:
4 Authors:
5
5
6 * Fernando Perez
6 * Fernando Perez
7 * Brian E. Granger
7 * Brian E. Granger
8 """
8 """
9
9
10 #-----------------------------------------------------------------------------
10 #-----------------------------------------------------------------------------
11 # Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu>
11 # Copyright (C) 2001-2007 Fernando Perez. <fperez@colorado.edu>
12 # Copyright (C) 2008-2011 The IPython Development Team
12 # Copyright (C) 2008-2011 The IPython Development Team
13 #
13 #
14 # Distributed under the terms of the BSD License. The full license is in
14 # Distributed under the terms of the BSD License. The full license is in
15 # the file COPYING, distributed as part of this software.
15 # the file COPYING, distributed as part of this software.
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 # Imports
19 # Imports
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22 import os
22 import os
23 import sys
23 import sys
24 import traceback
24 import traceback
25 from pprint import pformat
25 from pprint import pformat
26 from pathlib import Path
26 from pathlib import Path
27
27
28 from IPython.core import ultratb
28 from IPython.core import ultratb
29 from IPython.core.release import author_email
29 from IPython.core.release import author_email
30 from IPython.utils.sysinfo import sys_info
30 from IPython.utils.sysinfo import sys_info
31 from IPython.utils.py3compat import input
31 from IPython.utils.py3compat import input
32
32
33 from IPython.core.release import __version__ as version
33 from IPython.core.release import __version__ as version
34
34
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36 # Code
36 # Code
37 #-----------------------------------------------------------------------------
37 #-----------------------------------------------------------------------------
38
38
39 # Template for the user message.
39 # Template for the user message.
40 _default_message_template = """\
40 _default_message_template = """\
41 Oops, {app_name} crashed. We do our best to make it stable, but...
41 Oops, {app_name} crashed. We do our best to make it stable, but...
42
42
43 A crash report was automatically generated with the following information:
43 A crash report was automatically generated with the following information:
44 - A verbatim copy of the crash traceback.
44 - A verbatim copy of the crash traceback.
45 - A copy of your input history during this session.
45 - A copy of your input history during this session.
46 - Data on your current {app_name} configuration.
46 - Data on your current {app_name} configuration.
47
47
48 It was left in the file named:
48 It was left in the file named:
49 \t'{crash_report_fname}'
49 \t'{crash_report_fname}'
50 If you can email this file to the developers, the information in it will help
50 If you can email this file to the developers, the information in it will help
51 them in understanding and correcting the problem.
51 them in understanding and correcting the problem.
52
52
53 You can mail it to: {contact_name} at {contact_email}
53 You can mail it to: {contact_name} at {contact_email}
54 with the subject '{app_name} Crash Report'.
54 with the subject '{app_name} Crash Report'.
55
55
56 If you want to do it now, the following command will work (under Unix):
56 If you want to do it now, the following command will work (under Unix):
57 mail -s '{app_name} Crash Report' {contact_email} < {crash_report_fname}
57 mail -s '{app_name} Crash Report' {contact_email} < {crash_report_fname}
58
58
59 In your email, please also include information about:
59 In your email, please also include information about:
60 - The operating system under which the crash happened: Linux, macOS, Windows,
60 - The operating system under which the crash happened: Linux, macOS, Windows,
61 other, and which exact version (for example: Ubuntu 16.04.3, macOS 10.13.2,
61 other, and which exact version (for example: Ubuntu 16.04.3, macOS 10.13.2,
62 Windows 10 Pro), and whether it is 32-bit or 64-bit;
62 Windows 10 Pro), and whether it is 32-bit or 64-bit;
63 - How {app_name} was installed: using pip or conda, from GitHub, as part of
63 - How {app_name} was installed: using pip or conda, from GitHub, as part of
64 a Docker container, or other, providing more detail if possible;
64 a Docker container, or other, providing more detail if possible;
65 - How to reproduce the crash: what exact sequence of instructions can one
65 - How to reproduce the crash: what exact sequence of instructions can one
66 input to get the same crash? Ideally, find a minimal yet complete sequence
66 input to get the same crash? Ideally, find a minimal yet complete sequence
67 of instructions that yields the crash.
67 of instructions that yields the crash.
68
68
69 To ensure accurate tracking of this issue, please file a report about it at:
69 To ensure accurate tracking of this issue, please file a report about it at:
70 {bug_tracker}
70 {bug_tracker}
71 """
71 """
72
72
73 _lite_message_template = """
73 _lite_message_template = """
74 If you suspect this is an IPython {version} bug, please report it at:
74 If you suspect this is an IPython {version} bug, please report it at:
75 https://github.com/ipython/ipython/issues
75 https://github.com/ipython/ipython/issues
76 or send an email to the mailing list at {email}
76 or send an email to the mailing list at {email}
77
77
78 You can print a more detailed traceback right now with "%tb", or use "%debug"
78 You can print a more detailed traceback right now with "%tb", or use "%debug"
79 to interactively debug it.
79 to interactively debug it.
80
80
81 Extra-detailed tracebacks for bug-reporting purposes can be enabled via:
81 Extra-detailed tracebacks for bug-reporting purposes can be enabled via:
82 {config}Application.verbose_crash=True
82 {config}Application.verbose_crash=True
83 """
83 """
84
84
85
85
86 class CrashHandler(object):
86 class CrashHandler(object):
87 """Customizable crash handlers for IPython applications.
87 """Customizable crash handlers for IPython applications.
88
88
89 Instances of this class provide a :meth:`__call__` method which can be
89 Instances of this class provide a :meth:`__call__` method which can be
90 used as a ``sys.excepthook``. The :meth:`__call__` signature is::
90 used as a ``sys.excepthook``. The :meth:`__call__` signature is::
91
91
92 def __call__(self, etype, evalue, etb)
92 def __call__(self, etype, evalue, etb)
93 """
93 """
94
94
95 message_template = _default_message_template
95 message_template = _default_message_template
96 section_sep = '\n\n'+'*'*75+'\n\n'
96 section_sep = '\n\n'+'*'*75+'\n\n'
97
97
98 def __init__(self, app, contact_name=None, contact_email=None,
98 def __init__(self, app, contact_name=None, contact_email=None,
99 bug_tracker=None, show_crash_traceback=True, call_pdb=False):
99 bug_tracker=None, show_crash_traceback=True, call_pdb=False):
100 """Create a new crash handler
100 """Create a new crash handler
101
101
102 Parameters
102 Parameters
103 ----------
103 ----------
104 app : Application
104 app : Application
105 A running :class:`Application` instance, which will be queried at
105 A running :class:`Application` instance, which will be queried at
106 crash time for internal information.
106 crash time for internal information.
107
108 contact_name : str
107 contact_name : str
109 A string with the name of the person to contact.
108 A string with the name of the person to contact.
110
111 contact_email : str
109 contact_email : str
112 A string with the email address of the contact.
110 A string with the email address of the contact.
113
114 bug_tracker : str
111 bug_tracker : str
115 A string with the URL for your project's bug tracker.
112 A string with the URL for your project's bug tracker.
116
117 show_crash_traceback : bool
113 show_crash_traceback : bool
118 If false, don't print the crash traceback on stderr, only generate
114 If false, don't print the crash traceback on stderr, only generate
119 the on-disk report
115 the on-disk report
120
116 Non-argument instance attributes
121 Non-argument instance attributes:
122
123 These instances contain some non-argument attributes which allow for
117 These instances contain some non-argument attributes which allow for
124 further customization of the crash handler's behavior. Please see the
118 further customization of the crash handler's behavior. Please see the
125 source for further details.
119 source for further details.
126 """
120 """
127 self.crash_report_fname = "Crash_report_%s.txt" % app.name
121 self.crash_report_fname = "Crash_report_%s.txt" % app.name
128 self.app = app
122 self.app = app
129 self.call_pdb = call_pdb
123 self.call_pdb = call_pdb
130 #self.call_pdb = True # dbg
124 #self.call_pdb = True # dbg
131 self.show_crash_traceback = show_crash_traceback
125 self.show_crash_traceback = show_crash_traceback
132 self.info = dict(app_name = app.name,
126 self.info = dict(app_name = app.name,
133 contact_name = contact_name,
127 contact_name = contact_name,
134 contact_email = contact_email,
128 contact_email = contact_email,
135 bug_tracker = bug_tracker,
129 bug_tracker = bug_tracker,
136 crash_report_fname = self.crash_report_fname)
130 crash_report_fname = self.crash_report_fname)
137
131
138
132
139 def __call__(self, etype, evalue, etb):
133 def __call__(self, etype, evalue, etb):
140 """Handle an exception, call for compatible with sys.excepthook"""
134 """Handle an exception, call for compatible with sys.excepthook"""
141
135
142 # do not allow the crash handler to be called twice without reinstalling it
136 # do not allow the crash handler to be called twice without reinstalling it
143 # this prevents unlikely errors in the crash handling from entering an
137 # this prevents unlikely errors in the crash handling from entering an
144 # infinite loop.
138 # infinite loop.
145 sys.excepthook = sys.__excepthook__
139 sys.excepthook = sys.__excepthook__
146
140
147 # Report tracebacks shouldn't use color in general (safer for users)
141 # Report tracebacks shouldn't use color in general (safer for users)
148 color_scheme = 'NoColor'
142 color_scheme = 'NoColor'
149
143
150 # Use this ONLY for developer debugging (keep commented out for release)
144 # Use this ONLY for developer debugging (keep commented out for release)
151 #color_scheme = 'Linux' # dbg
145 #color_scheme = 'Linux' # dbg
152 try:
146 try:
153 rptdir = self.app.ipython_dir
147 rptdir = self.app.ipython_dir
154 except:
148 except:
155 rptdir = Path.cwd()
149 rptdir = Path.cwd()
156 if rptdir is None or not Path.is_dir(rptdir):
150 if rptdir is None or not Path.is_dir(rptdir):
157 rptdir = Path.cwd()
151 rptdir = Path.cwd()
158 report_name = rptdir / self.crash_report_fname
152 report_name = rptdir / self.crash_report_fname
159 # write the report filename into the instance dict so it can get
153 # write the report filename into the instance dict so it can get
160 # properly expanded out in the user message template
154 # properly expanded out in the user message template
161 self.crash_report_fname = report_name
155 self.crash_report_fname = report_name
162 self.info['crash_report_fname'] = report_name
156 self.info['crash_report_fname'] = report_name
163 TBhandler = ultratb.VerboseTB(
157 TBhandler = ultratb.VerboseTB(
164 color_scheme=color_scheme,
158 color_scheme=color_scheme,
165 long_header=1,
159 long_header=1,
166 call_pdb=self.call_pdb,
160 call_pdb=self.call_pdb,
167 )
161 )
168 if self.call_pdb:
162 if self.call_pdb:
169 TBhandler(etype,evalue,etb)
163 TBhandler(etype,evalue,etb)
170 return
164 return
171 else:
165 else:
172 traceback = TBhandler.text(etype,evalue,etb,context=31)
166 traceback = TBhandler.text(etype,evalue,etb,context=31)
173
167
174 # print traceback to screen
168 # print traceback to screen
175 if self.show_crash_traceback:
169 if self.show_crash_traceback:
176 print(traceback, file=sys.stderr)
170 print(traceback, file=sys.stderr)
177
171
178 # and generate a complete report on disk
172 # and generate a complete report on disk
179 try:
173 try:
180 report = open(report_name,'w')
174 report = open(report_name,'w')
181 except:
175 except:
182 print('Could not create crash report on disk.', file=sys.stderr)
176 print('Could not create crash report on disk.', file=sys.stderr)
183 return
177 return
184
178
185 with report:
179 with report:
186 # Inform user on stderr of what happened
180 # Inform user on stderr of what happened
187 print('\n'+'*'*70+'\n', file=sys.stderr)
181 print('\n'+'*'*70+'\n', file=sys.stderr)
188 print(self.message_template.format(**self.info), file=sys.stderr)
182 print(self.message_template.format(**self.info), file=sys.stderr)
189
183
190 # Construct report on disk
184 # Construct report on disk
191 report.write(self.make_report(traceback))
185 report.write(self.make_report(traceback))
192
186
193 input("Hit <Enter> to quit (your terminal may close):")
187 input("Hit <Enter> to quit (your terminal may close):")
194
188
195 def make_report(self,traceback):
189 def make_report(self,traceback):
196 """Return a string containing a crash report."""
190 """Return a string containing a crash report."""
197
191
198 sec_sep = self.section_sep
192 sec_sep = self.section_sep
199
193
200 report = ['*'*75+'\n\n'+'IPython post-mortem report\n\n']
194 report = ['*'*75+'\n\n'+'IPython post-mortem report\n\n']
201 rpt_add = report.append
195 rpt_add = report.append
202 rpt_add(sys_info())
196 rpt_add(sys_info())
203
197
204 try:
198 try:
205 config = pformat(self.app.config)
199 config = pformat(self.app.config)
206 rpt_add(sec_sep)
200 rpt_add(sec_sep)
207 rpt_add('Application name: %s\n\n' % self.app_name)
201 rpt_add('Application name: %s\n\n' % self.app_name)
208 rpt_add('Current user configuration structure:\n\n')
202 rpt_add('Current user configuration structure:\n\n')
209 rpt_add(config)
203 rpt_add(config)
210 except:
204 except:
211 pass
205 pass
212 rpt_add(sec_sep+'Crash traceback:\n\n' + traceback)
206 rpt_add(sec_sep+'Crash traceback:\n\n' + traceback)
213
207
214 return ''.join(report)
208 return ''.join(report)
215
209
216
210
217 def crash_handler_lite(etype, evalue, tb):
211 def crash_handler_lite(etype, evalue, tb):
218 """a light excepthook, adding a small message to the usual traceback"""
212 """a light excepthook, adding a small message to the usual traceback"""
219 traceback.print_exception(etype, evalue, tb)
213 traceback.print_exception(etype, evalue, tb)
220
214
221 from IPython.core.interactiveshell import InteractiveShell
215 from IPython.core.interactiveshell import InteractiveShell
222 if InteractiveShell.initialized():
216 if InteractiveShell.initialized():
223 # we are in a Shell environment, give %magic example
217 # we are in a Shell environment, give %magic example
224 config = "%config "
218 config = "%config "
225 else:
219 else:
226 # we are not in a shell, show generic config
220 # we are not in a shell, show generic config
227 config = "c."
221 config = "c."
228 print(_lite_message_template.format(email=author_email, config=config, version=version), file=sys.stderr)
222 print(_lite_message_template.format(email=author_email, config=config, version=version), file=sys.stderr)
229
223
@@ -1,857 +1,972 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 Pdb debugger class.
3 Pdb debugger class.
4
4
5 Modified from the standard pdb.Pdb class to avoid including readline, so that
5 Modified from the standard pdb.Pdb class to avoid including readline, so that
6 the command line completion of other programs which include this isn't
6 the command line completion of other programs which include this isn't
7 damaged.
7 damaged.
8
8
9 In the future, this class will be expanded with improvements over the standard
9 In the future, this class will be expanded with improvements over the standard
10 pdb.
10 pdb.
11
11
12 The code in this file is mainly lifted out of cmd.py in Python 2.2, with minor
12 The code in this file is mainly lifted out of cmd.py in Python 2.2, with minor
13 changes. Licensing should therefore be under the standard Python terms. For
13 changes. Licensing should therefore be under the standard Python terms. For
14 details on the PSF (Python Software Foundation) standard license, see:
14 details on the PSF (Python Software Foundation) standard license, see:
15
15
16 https://docs.python.org/2/license.html
16 https://docs.python.org/2/license.html
17 """
17 """
18
18
19 #*****************************************************************************
19 #*****************************************************************************
20 #
20 #
21 # This file is licensed under the PSF license.
21 # This file is licensed under the PSF license.
22 #
22 #
23 # Copyright (C) 2001 Python Software Foundation, www.python.org
23 # Copyright (C) 2001 Python Software Foundation, www.python.org
24 # Copyright (C) 2005-2006 Fernando Perez. <fperez@colorado.edu>
24 # Copyright (C) 2005-2006 Fernando Perez. <fperez@colorado.edu>
25 #
25 #
26 #
26 #
27 #*****************************************************************************
27 #*****************************************************************************
28
28
29 import bdb
29 import bdb
30 import functools
30 import functools
31 import inspect
31 import inspect
32 import linecache
32 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
39 from IPython.utils import coloransi, py3compat
40 from IPython.utils import coloransi, py3compat
40 from IPython.core.excolors import exception_colors
41 from IPython.core.excolors import exception_colors
41 from IPython.testing.skipdoctest import skip_doctest
42 from IPython.testing.skipdoctest import skip_doctest
42
43
43
44
44 prompt = 'ipdb> '
45 prompt = 'ipdb> '
45
46
46 # We have to check this directly from sys.argv, config struct not yet available
47 # We have to check this directly from sys.argv, config struct not yet available
47 from pdb import Pdb as OldPdb
48 from pdb import Pdb as OldPdb
48
49
49 # Allow the set_trace code to operate outside of an ipython instance, even if
50 # Allow the set_trace code to operate outside of an ipython instance, even if
50 # it does so with some limitations. The rest of this support is implemented in
51 # it does so with some limitations. The rest of this support is implemented in
51 # the Tracer constructor.
52 # the Tracer constructor.
52
53
53
54
54 def make_arrow(pad):
55 def make_arrow(pad):
55 """generate the leading arrow in front of traceback or debugger"""
56 """generate the leading arrow in front of traceback or debugger"""
56 if pad >= 2:
57 if pad >= 2:
57 return '-'*(pad-2) + '> '
58 return '-'*(pad-2) + '> '
58 elif pad == 1:
59 elif pad == 1:
59 return '>'
60 return '>'
60 return ''
61 return ''
61
62
62
63
63 def BdbQuit_excepthook(et, ev, tb, excepthook=None):
64 def BdbQuit_excepthook(et, ev, tb, excepthook=None):
64 """Exception hook which handles `BdbQuit` exceptions.
65 """Exception hook which handles `BdbQuit` exceptions.
65
66
66 All other exceptions are processed using the `excepthook`
67 All other exceptions are processed using the `excepthook`
67 parameter.
68 parameter.
68 """
69 """
69 warnings.warn("`BdbQuit_excepthook` is deprecated since version 5.1",
70 warnings.warn("`BdbQuit_excepthook` is deprecated since version 5.1",
70 DeprecationWarning, stacklevel=2)
71 DeprecationWarning, stacklevel=2)
71 if et == bdb.BdbQuit:
72 if et == bdb.BdbQuit:
72 print('Exiting Debugger.')
73 print('Exiting Debugger.')
73 elif excepthook is not None:
74 elif excepthook is not None:
74 excepthook(et, ev, tb)
75 excepthook(et, ev, tb)
75 else:
76 else:
76 # Backwards compatibility. Raise deprecation warning?
77 # Backwards compatibility. Raise deprecation warning?
77 BdbQuit_excepthook.excepthook_ori(et, ev, tb)
78 BdbQuit_excepthook.excepthook_ori(et, ev, tb)
78
79
79
80
80 def BdbQuit_IPython_excepthook(self, et, ev, tb, tb_offset=None):
81 def BdbQuit_IPython_excepthook(self, et, ev, tb, tb_offset=None):
81 warnings.warn(
82 warnings.warn(
82 "`BdbQuit_IPython_excepthook` is deprecated since version 5.1",
83 "`BdbQuit_IPython_excepthook` is deprecated since version 5.1",
83 DeprecationWarning, stacklevel=2)
84 DeprecationWarning, stacklevel=2)
84 print('Exiting Debugger.')
85 print('Exiting Debugger.')
85
86
86
87
87 class Tracer(object):
88 class Tracer(object):
88 """
89 """
89 DEPRECATED
90 DEPRECATED
90
91
91 Class for local debugging, similar to pdb.set_trace.
92 Class for local debugging, similar to pdb.set_trace.
92
93
93 Instances of this class, when called, behave like pdb.set_trace, but
94 Instances of this class, when called, behave like pdb.set_trace, but
94 providing IPython's enhanced capabilities.
95 providing IPython's enhanced capabilities.
95
96
96 This is implemented as a class which must be initialized in your own code
97 This is implemented as a class which must be initialized in your own code
97 and not as a standalone function because we need to detect at runtime
98 and not as a standalone function because we need to detect at runtime
98 whether IPython is already active or not. That detection is done in the
99 whether IPython is already active or not. That detection is done in the
99 constructor, ensuring that this code plays nicely with a running IPython,
100 constructor, ensuring that this code plays nicely with a running IPython,
100 while functioning acceptably (though with limitations) if outside of it.
101 while functioning acceptably (though with limitations) if outside of it.
101 """
102 """
102
103
103 @skip_doctest
104 @skip_doctest
104 def __init__(self, colors=None):
105 def __init__(self, colors=None):
105 """
106 """
106 DEPRECATED
107 DEPRECATED
107
108
108 Create a local debugger instance.
109 Create a local debugger instance.
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
116 the current IPython scheme when running inside IPython, and to
116 the current IPython scheme when running inside IPython, and to
117 'NoColor' otherwise.
117 'NoColor' otherwise.
118
118
119 Examples
119 Examples
120 --------
120 --------
121 ::
121 ::
122
122
123 from IPython.core.debugger import Tracer; debug_here = Tracer()
123 from IPython.core.debugger import Tracer; debug_here = Tracer()
124
124
125 Later in your code::
125 Later in your code::
126
126
127 debug_here() # -> will open up the debugger at that point.
127 debug_here() # -> will open up the debugger at that point.
128
128
129 Once the debugger activates, you can use all of its regular commands to
129 Once the debugger activates, you can use all of its regular commands to
130 step through code, set breakpoints, etc. See the pdb documentation
130 step through code, set breakpoints, etc. See the pdb documentation
131 from the Python standard library for usage details.
131 from the Python standard library for usage details.
132 """
132 """
133 warnings.warn("`Tracer` is deprecated since version 5.1, directly use "
133 warnings.warn("`Tracer` is deprecated since version 5.1, directly use "
134 "`IPython.core.debugger.Pdb.set_trace()`",
134 "`IPython.core.debugger.Pdb.set_trace()`",
135 DeprecationWarning, stacklevel=2)
135 DeprecationWarning, stacklevel=2)
136
136
137 ip = get_ipython()
137 ip = get_ipython()
138 if ip is None:
138 if ip is None:
139 # Outside of ipython, we set our own exception hook manually
139 # Outside of ipython, we set our own exception hook manually
140 sys.excepthook = functools.partial(BdbQuit_excepthook,
140 sys.excepthook = functools.partial(BdbQuit_excepthook,
141 excepthook=sys.excepthook)
141 excepthook=sys.excepthook)
142 def_colors = 'NoColor'
142 def_colors = 'NoColor'
143 else:
143 else:
144 # In ipython, we use its custom exception handler mechanism
144 # In ipython, we use its custom exception handler mechanism
145 def_colors = ip.colors
145 def_colors = ip.colors
146 ip.set_custom_exc((bdb.BdbQuit,), BdbQuit_IPython_excepthook)
146 ip.set_custom_exc((bdb.BdbQuit,), BdbQuit_IPython_excepthook)
147
147
148 if colors is None:
148 if colors is None:
149 colors = def_colors
149 colors = def_colors
150
150
151 # The stdlib debugger internally uses a modified repr from the `repr`
151 # The stdlib debugger internally uses a modified repr from the `repr`
152 # module, that limits the length of printed strings to a hardcoded
152 # module, that limits the length of printed strings to a hardcoded
153 # limit of 30 characters. That much trimming is too aggressive, let's
153 # limit of 30 characters. That much trimming is too aggressive, let's
154 # at least raise that limit to 80 chars, which should be enough for
154 # at least raise that limit to 80 chars, which should be enough for
155 # most interactive uses.
155 # most interactive uses.
156 try:
156 try:
157 from reprlib import aRepr
157 from reprlib import aRepr
158 aRepr.maxstring = 80
158 aRepr.maxstring = 80
159 except:
159 except:
160 # This is only a user-facing convenience, so any error we encounter
160 # This is only a user-facing convenience, so any error we encounter
161 # here can be warned about but can be otherwise ignored. These
161 # here can be warned about but can be otherwise ignored. These
162 # printouts will tell us about problems if this API changes
162 # printouts will tell us about problems if this API changes
163 import traceback
163 import traceback
164 traceback.print_exc()
164 traceback.print_exc()
165
165
166 self.debugger = Pdb(colors)
166 self.debugger = Pdb(colors)
167
167
168 def __call__(self):
168 def __call__(self):
169 """Starts an interactive debugger at the point where called.
169 """Starts an interactive debugger at the point where called.
170
170
171 This is similar to the pdb.set_trace() function from the std lib, but
171 This is similar to the pdb.set_trace() function from the std lib, but
172 using IPython's enhanced debugger."""
172 using IPython's enhanced debugger."""
173
173
174 self.debugger.set_trace(sys._getframe().f_back)
174 self.debugger.set_trace(sys._getframe().f_back)
175
175
176
176
177 RGX_EXTRA_INDENT = re.compile(r'(?<=\n)\s+')
177 RGX_EXTRA_INDENT = re.compile(r'(?<=\n)\s+')
178
178
179
179
180 def strip_indentation(multiline_string):
180 def strip_indentation(multiline_string):
181 return RGX_EXTRA_INDENT.sub('', multiline_string)
181 return RGX_EXTRA_INDENT.sub('', multiline_string)
182
182
183
183
184 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
184 def decorate_fn_with_doc(new_fn, old_fn, additional_text=""):
185 """Make new_fn have old_fn's doc string. This is particularly useful
185 """Make new_fn have old_fn's doc string. This is particularly useful
186 for the ``do_...`` commands that hook into the help system.
186 for the ``do_...`` commands that hook into the help system.
187 Adapted from from a comp.lang.python posting
187 Adapted from from a comp.lang.python posting
188 by Duncan Booth."""
188 by Duncan Booth."""
189 def wrapper(*args, **kw):
189 def wrapper(*args, **kw):
190 return new_fn(*args, **kw)
190 return new_fn(*args, **kw)
191 if old_fn.__doc__:
191 if old_fn.__doc__:
192 wrapper.__doc__ = strip_indentation(old_fn.__doc__) + additional_text
192 wrapper.__doc__ = strip_indentation(old_fn.__doc__) + additional_text
193 return wrapper
193 return wrapper
194
194
195
195
196 class Pdb(OldPdb):
196 class Pdb(OldPdb):
197 """Modified Pdb class, does not load readline.
197 """Modified Pdb class, does not load readline.
198
198
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
215 The possibilities are python version dependent, see the python
229 Passed to pdb.Pdb.
216 docs for more info.
230
231 Notes
232 -----
233 The possibilities are python version dependent, see the python
234 docs for more info.
217 """
235 """
218
236
219 # Parent constructor:
237 # Parent constructor:
220 try:
238 try:
221 self.context = int(context)
239 self.context = int(context)
222 if self.context <= 0:
240 if self.context <= 0:
223 raise ValueError("Context must be a positive integer")
241 raise ValueError("Context must be a positive integer")
224 except (TypeError, ValueError) as e:
242 except (TypeError, ValueError) as e:
225 raise ValueError("Context must be a positive integer") from e
243 raise ValueError("Context must be a positive integer") from e
226
244
227 # `kwargs` ensures full compatibility with stdlib's `pdb.Pdb`.
245 # `kwargs` ensures full compatibility with stdlib's `pdb.Pdb`.
228 OldPdb.__init__(self, completekey, stdin, stdout, **kwargs)
246 OldPdb.__init__(self, completekey, stdin, stdout, **kwargs)
229
247
230 # IPython changes...
248 # IPython changes...
231 self.shell = get_ipython()
249 self.shell = get_ipython()
232
250
233 if self.shell is None:
251 if self.shell is None:
234 save_main = sys.modules['__main__']
252 save_main = sys.modules['__main__']
235 # No IPython instance running, we must create one
253 # No IPython instance running, we must create one
236 from IPython.terminal.interactiveshell import \
254 from IPython.terminal.interactiveshell import \
237 TerminalInteractiveShell
255 TerminalInteractiveShell
238 self.shell = TerminalInteractiveShell.instance()
256 self.shell = TerminalInteractiveShell.instance()
239 # needed by any code which calls __import__("__main__") after
257 # needed by any code which calls __import__("__main__") after
240 # the debugger was entered. See also #9941.
258 # the debugger was entered. See also #9941.
241 sys.modules["__main__"] = save_main
259 sys.modules["__main__"] = save_main
242
260
243 if color_scheme is not None:
261 if color_scheme is not None:
244 warnings.warn(
262 warnings.warn(
245 "The `color_scheme` argument is deprecated since version 5.1",
263 "The `color_scheme` argument is deprecated since version 5.1",
246 DeprecationWarning, stacklevel=2)
264 DeprecationWarning, stacklevel=2)
247 else:
265 else:
248 color_scheme = self.shell.colors
266 color_scheme = self.shell.colors
249
267
250 self.aliases = {}
268 self.aliases = {}
251
269
252 # Create color table: we copy the default one from the traceback
270 # Create color table: we copy the default one from the traceback
253 # module and add a few attributes needed for debugging
271 # module and add a few attributes needed for debugging
254 self.color_scheme_table = exception_colors()
272 self.color_scheme_table = exception_colors()
255
273
256 # shorthands
274 # shorthands
257 C = coloransi.TermColors
275 C = coloransi.TermColors
258 cst = self.color_scheme_table
276 cst = self.color_scheme_table
259
277
260 cst['NoColor'].colors.prompt = C.NoColor
278 cst['NoColor'].colors.prompt = C.NoColor
261 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
279 cst['NoColor'].colors.breakpoint_enabled = C.NoColor
262 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
280 cst['NoColor'].colors.breakpoint_disabled = C.NoColor
263
281
264 cst['Linux'].colors.prompt = C.Green
282 cst['Linux'].colors.prompt = C.Green
265 cst['Linux'].colors.breakpoint_enabled = C.LightRed
283 cst['Linux'].colors.breakpoint_enabled = C.LightRed
266 cst['Linux'].colors.breakpoint_disabled = C.Red
284 cst['Linux'].colors.breakpoint_disabled = C.Red
267
285
268 cst['LightBG'].colors.prompt = C.Blue
286 cst['LightBG'].colors.prompt = C.Blue
269 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
287 cst['LightBG'].colors.breakpoint_enabled = C.LightRed
270 cst['LightBG'].colors.breakpoint_disabled = C.Red
288 cst['LightBG'].colors.breakpoint_disabled = C.Red
271
289
272 cst['Neutral'].colors.prompt = C.Blue
290 cst['Neutral'].colors.prompt = C.Blue
273 cst['Neutral'].colors.breakpoint_enabled = C.LightRed
291 cst['Neutral'].colors.breakpoint_enabled = C.LightRed
274 cst['Neutral'].colors.breakpoint_disabled = C.Red
292 cst['Neutral'].colors.breakpoint_disabled = C.Red
275
293
276 # Add a python parser so we can syntax highlight source while
294 # Add a python parser so we can syntax highlight source while
277 # debugging.
295 # debugging.
278 self.parser = PyColorize.Parser(style=color_scheme)
296 self.parser = PyColorize.Parser(style=color_scheme)
279 self.set_colors(color_scheme)
297 self.set_colors(color_scheme)
280
298
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."""
287 self.color_scheme_table.set_active_scheme(scheme)
309 self.color_scheme_table.set_active_scheme(scheme)
288 self.parser.style = scheme
310 self.parser.style = scheme
289
311
290 def set_trace(self, frame=None):
312 def set_trace(self, frame=None):
291 if frame is None:
313 if frame is None:
292 frame = sys._getframe().f_back
314 frame = sys._getframe().f_back
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.
299
341
300 This is used in up/down and where to skip frames.
342 This is used in up/down and where to skip frames.
301 """
343 """
302 # The f_locals dictionary is updated from the actual frame
344 # The f_locals dictionary is updated from the actual frame
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
317 def interaction(self, frame, traceback):
354 def interaction(self, frame, traceback):
318 try:
355 try:
319 OldPdb.interaction(self, frame, traceback)
356 OldPdb.interaction(self, frame, traceback)
320 except KeyboardInterrupt:
357 except KeyboardInterrupt:
321 self.stdout.write("\n" + self.shell.get_exception_only())
358 self.stdout.write("\n" + self.shell.get_exception_only())
322
359
323 def precmd(self, line):
360 def precmd(self, line):
324 """Perform useful escapes on the command before it is executed."""
361 """Perform useful escapes on the command before it is executed."""
325
362
326 if line.endswith("??"):
363 if line.endswith("??"):
327 line = "pinfo2 " + line[:-2]
364 line = "pinfo2 " + line[:-2]
328 elif line.endswith("?"):
365 elif line.endswith("?"):
329 line = "pinfo " + line[:-1]
366 line = "pinfo " + line[:-1]
330
367
331 line = super().precmd(line)
368 line = super().precmd(line)
332
369
333 return line
370 return line
334
371
335 def new_do_frame(self, arg):
372 def new_do_frame(self, arg):
336 OldPdb.do_frame(self, arg)
373 OldPdb.do_frame(self, arg)
337
374
338 def new_do_quit(self, arg):
375 def new_do_quit(self, arg):
339
376
340 if hasattr(self, 'old_all_completions'):
377 if hasattr(self, 'old_all_completions'):
341 self.shell.Completer.all_completions = self.old_all_completions
378 self.shell.Completer.all_completions = self.old_all_completions
342
379
343 return OldPdb.do_quit(self, arg)
380 return OldPdb.do_quit(self, arg)
344
381
345 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
382 do_q = do_quit = decorate_fn_with_doc(new_do_quit, OldPdb.do_quit)
346
383
347 def new_do_restart(self, arg):
384 def new_do_restart(self, arg):
348 """Restart command. In the context of ipython this is exactly the same
385 """Restart command. In the context of ipython this is exactly the same
349 thing as 'quit'."""
386 thing as 'quit'."""
350 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
387 self.msg("Restart doesn't make sense here. Using 'quit' instead.")
351 return self.do_quit(arg)
388 return self.do_quit(arg)
352
389
353 def print_stack_trace(self, context=None):
390 def print_stack_trace(self, context=None):
354 Colors = self.color_scheme_table.active_colors
391 Colors = self.color_scheme_table.active_colors
355 ColorsNormal = Colors.Normal
392 ColorsNormal = Colors.Normal
356 if context is None:
393 if context is None:
357 context = self.context
394 context = self.context
358 try:
395 try:
359 context = int(context)
396 context = int(context)
360 if context <= 0:
397 if context <= 0:
361 raise ValueError("Context must be a positive integer")
398 raise ValueError("Context must be a positive integer")
362 except (TypeError, ValueError) as e:
399 except (TypeError, ValueError) as e:
363 raise ValueError("Context must be a positive integer") from e
400 raise ValueError("Context must be a positive integer") from e
364 try:
401 try:
365 skipped = 0
402 skipped = 0
366 for hidden, frame_lineno in zip(self.hidden_frames(self.stack), self.stack):
403 for hidden, frame_lineno in zip(self.hidden_frames(self.stack), self.stack):
367 if hidden and self.skip_hidden:
404 if hidden and self.skip_hidden:
368 skipped += 1
405 skipped += 1
369 continue
406 continue
370 if skipped:
407 if skipped:
371 print(
408 print(
372 f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n"
409 f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n"
373 )
410 )
374 skipped = 0
411 skipped = 0
375 self.print_stack_entry(frame_lineno, context=context)
412 self.print_stack_entry(frame_lineno, context=context)
376 if skipped:
413 if skipped:
377 print(
414 print(
378 f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n"
415 f"{Colors.excName} [... skipping {skipped} hidden frame(s)]{ColorsNormal}\n"
379 )
416 )
380 except KeyboardInterrupt:
417 except KeyboardInterrupt:
381 pass
418 pass
382
419
383 def print_stack_entry(self, frame_lineno, prompt_prefix='\n-> ',
420 def print_stack_entry(self, frame_lineno, prompt_prefix='\n-> ',
384 context=None):
421 context=None):
385 if context is None:
422 if context is None:
386 context = self.context
423 context = self.context
387 try:
424 try:
388 context = int(context)
425 context = int(context)
389 if context <= 0:
426 if context <= 0:
390 raise ValueError("Context must be a positive integer")
427 raise ValueError("Context must be a positive integer")
391 except (TypeError, ValueError) as e:
428 except (TypeError, ValueError) as e:
392 raise ValueError("Context must be a positive integer") from e
429 raise ValueError("Context must be a positive integer") from e
393 print(self.format_stack_entry(frame_lineno, '', context), file=self.stdout)
430 print(self.format_stack_entry(frame_lineno, '', context), file=self.stdout)
394
431
395 # vds: >>
432 # vds: >>
396 frame, lineno = frame_lineno
433 frame, lineno = frame_lineno
397 filename = frame.f_code.co_filename
434 filename = frame.f_code.co_filename
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
404 try:
463 try:
405 context = int(context)
464 context = int(context)
406 if context <= 0:
465 if context <= 0:
407 print("Context must be a positive integer", file=self.stdout)
466 print("Context must be a positive integer", file=self.stdout)
408 except (TypeError, ValueError):
467 except (TypeError, ValueError):
409 print("Context must be a positive integer", file=self.stdout)
468 print("Context must be a positive integer", file=self.stdout)
410
469
411 import reprlib
470 import reprlib
412
471
413 ret = []
472 ret = []
414
473
415 Colors = self.color_scheme_table.active_colors
474 Colors = self.color_scheme_table.active_colors
416 ColorsNormal = Colors.Normal
475 ColorsNormal = Colors.Normal
417 tpl_link = "%s%%s%s" % (Colors.filenameEm, ColorsNormal)
476 tpl_link = "%s%%s%s" % (Colors.filenameEm, ColorsNormal)
418 tpl_call = "%s%%s%s%%s%s" % (Colors.vName, Colors.valEm, ColorsNormal)
477 tpl_call = "%s%%s%s%%s%s" % (Colors.vName, Colors.valEm, ColorsNormal)
419 tpl_line = "%%s%s%%s %s%%s" % (Colors.lineno, ColorsNormal)
478 tpl_line = "%%s%s%%s %s%%s" % (Colors.lineno, ColorsNormal)
420 tpl_line_em = "%%s%s%%s %s%%s%s" % (Colors.linenoEm, Colors.line, ColorsNormal)
479 tpl_line_em = "%%s%s%%s %s%%s%s" % (Colors.linenoEm, Colors.line, ColorsNormal)
421
480
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:
427 #return_value += '->'
486 rv = loc_frame["__return__"]
428 return_value += reprlib.repr(rv) + '\n'
487 # return_value += '->'
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` + ')'
432 filename = self.canonic(frame.f_code.co_filename)
492 filename = self.canonic(frame.f_code.co_filename)
433 link = tpl_link % py3compat.cast_unicode(filename)
493 link = tpl_link % py3compat.cast_unicode(filename)
434
494
435 if frame.f_code.co_name:
495 if frame.f_code.co_name:
436 func = frame.f_code.co_name
496 func = frame.f_code.co_name
437 else:
497 else:
438 func = "<lambda>"
498 func = "<lambda>"
439
499
440 call = ''
500 call = ""
441 if func != '?':
501 if func != "?":
442 if '__args__' in frame.f_locals:
502 if "__args__" in loc_frame:
443 args = reprlib.repr(frame.f_locals['__args__'])
503 args = reprlib.repr(loc_frame["__args__"])
444 else:
504 else:
445 args = '()'
505 args = '()'
446 call = tpl_call % (func, args)
506 call = tpl_call % (func, args)
447
507
448 # The level info should be generated in the same format pdb uses, to
508 # The level info should be generated in the same format pdb uses, to
449 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
509 # avoid breaking the pdbtrack functionality of python-mode in *emacs.
450 if frame is self.curframe:
510 if frame is self.curframe:
451 ret.append('> ')
511 ret.append('> ')
452 else:
512 else:
453 ret.append(" ")
513 ret.append(" ")
454 ret.append("%s(%s)%s\n" % (link, lineno, call))
514 ret.append("%s(%s)%s\n" % (link, lineno, call))
455
515
456 start = lineno - 1 - context//2
516 start = lineno - 1 - context//2
457 lines = linecache.getlines(filename)
517 lines = linecache.getlines(filename)
458 start = min(start, len(lines) - context)
518 start = min(start, len(lines) - context)
459 start = max(start, 0)
519 start = max(start, 0)
460 lines = lines[start : start + context]
520 lines = lines[start : start + context]
461
521
462 for i, line in enumerate(lines):
522 for i, line in enumerate(lines):
463 show_arrow = start + 1 + i == lineno
523 show_arrow = start + 1 + i == lineno
464 linetpl = (frame is self.curframe or show_arrow) and tpl_line_em or tpl_line
524 linetpl = (frame is self.curframe or show_arrow) and tpl_line_em or tpl_line
465 ret.append(
525 ret.append(
466 self.__format_line(
526 self.__format_line(
467 linetpl, filename, start + 1 + i, line, arrow=show_arrow
527 linetpl, filename, start + 1 + i, line, arrow=show_arrow
468 )
528 )
469 )
529 )
470 return "".join(ret)
530 return "".join(ret)
471
531
472 def __format_line(self, tpl_line, filename, lineno, line, arrow=False):
532 def __format_line(self, tpl_line, filename, lineno, line, arrow=False):
473 bp_mark = ""
533 bp_mark = ""
474 bp_mark_color = ""
534 bp_mark_color = ""
475
535
476 new_line, err = self.parser.format2(line, 'str')
536 new_line, err = self.parser.format2(line, 'str')
477 if not err:
537 if not err:
478 line = new_line
538 line = new_line
479
539
480 bp = None
540 bp = None
481 if lineno in self.get_file_breaks(filename):
541 if lineno in self.get_file_breaks(filename):
482 bps = self.get_breaks(filename, lineno)
542 bps = self.get_breaks(filename, lineno)
483 bp = bps[-1]
543 bp = bps[-1]
484
544
485 if bp:
545 if bp:
486 Colors = self.color_scheme_table.active_colors
546 Colors = self.color_scheme_table.active_colors
487 bp_mark = str(bp.number)
547 bp_mark = str(bp.number)
488 bp_mark_color = Colors.breakpoint_enabled
548 bp_mark_color = Colors.breakpoint_enabled
489 if not bp.enabled:
549 if not bp.enabled:
490 bp_mark_color = Colors.breakpoint_disabled
550 bp_mark_color = Colors.breakpoint_disabled
491
551
492 numbers_width = 7
552 numbers_width = 7
493 if arrow:
553 if arrow:
494 # This is the line with the error
554 # This is the line with the error
495 pad = numbers_width - len(str(lineno)) - len(bp_mark)
555 pad = numbers_width - len(str(lineno)) - len(bp_mark)
496 num = '%s%s' % (make_arrow(pad), str(lineno))
556 num = '%s%s' % (make_arrow(pad), str(lineno))
497 else:
557 else:
498 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
558 num = '%*s' % (numbers_width - len(bp_mark), str(lineno))
499
559
500 return tpl_line % (bp_mark_color + bp_mark, num, line)
560 return tpl_line % (bp_mark_color + bp_mark, num, line)
501
561
502 def print_list_lines(self, filename, first, last):
562 def print_list_lines(self, filename, first, last):
503 """The printing (as opposed to the parsing part of a 'list'
563 """The printing (as opposed to the parsing part of a 'list'
504 command."""
564 command."""
505 try:
565 try:
506 Colors = self.color_scheme_table.active_colors
566 Colors = self.color_scheme_table.active_colors
507 ColorsNormal = Colors.Normal
567 ColorsNormal = Colors.Normal
508 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
568 tpl_line = '%%s%s%%s %s%%s' % (Colors.lineno, ColorsNormal)
509 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
569 tpl_line_em = '%%s%s%%s %s%%s%s' % (Colors.linenoEm, Colors.line, ColorsNormal)
510 src = []
570 src = []
511 if filename == "<string>" and hasattr(self, "_exec_filename"):
571 if filename == "<string>" and hasattr(self, "_exec_filename"):
512 filename = self._exec_filename
572 filename = self._exec_filename
513
573
514 for lineno in range(first, last+1):
574 for lineno in range(first, last+1):
515 line = linecache.getline(filename, lineno)
575 line = linecache.getline(filename, lineno)
516 if not line:
576 if not line:
517 break
577 break
518
578
519 if lineno == self.curframe.f_lineno:
579 if lineno == self.curframe.f_lineno:
520 line = self.__format_line(
580 line = self.__format_line(
521 tpl_line_em, filename, lineno, line, arrow=True
581 tpl_line_em, filename, lineno, line, arrow=True
522 )
582 )
523 else:
583 else:
524 line = self.__format_line(
584 line = self.__format_line(
525 tpl_line, filename, lineno, line, arrow=False
585 tpl_line, filename, lineno, line, arrow=False
526 )
586 )
527
587
528 src.append(line)
588 src.append(line)
529 self.lineno = lineno
589 self.lineno = lineno
530
590
531 print(''.join(src), file=self.stdout)
591 print(''.join(src), file=self.stdout)
532
592
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
548 """
661 """
549 self.lastcmd = 'list'
662 self.lastcmd = 'list'
550 last = None
663 last = None
551 if arg:
664 if arg:
552 try:
665 try:
553 x = eval(arg, {}, {})
666 x = eval(arg, {}, {})
554 if type(x) == type(()):
667 if type(x) == type(()):
555 first, last = x
668 first, last = x
556 first = int(first)
669 first = int(first)
557 last = int(last)
670 last = int(last)
558 if last < first:
671 if last < first:
559 # Assume it's a count
672 # Assume it's a count
560 last = first + last
673 last = first + last
561 else:
674 else:
562 first = max(1, int(x) - 5)
675 first = max(1, int(x) - 5)
563 except:
676 except:
564 print('*** Error in argument:', repr(arg), file=self.stdout)
677 print('*** Error in argument:', repr(arg), file=self.stdout)
565 return
678 return
566 elif self.lineno is None:
679 elif self.lineno is None:
567 first = max(1, self.curframe.f_lineno - 5)
680 first = max(1, self.curframe.f_lineno - 5)
568 else:
681 else:
569 first = self.lineno + 1
682 first = self.lineno + 1
570 if last is None:
683 if last is None:
571 last = first + 10
684 last = first + 10
572 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
685 self.print_list_lines(self.curframe.f_code.co_filename, first, last)
573
686
574 # vds: >>
687 # vds: >>
575 lineno = first
688 lineno = first
576 filename = self.curframe.f_code.co_filename
689 filename = self.curframe.f_code.co_filename
577 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
690 self.shell.hooks.synchronize_with_editor(filename, lineno, 0)
578 # vds: <<
691 # vds: <<
579
692
580 do_l = do_list
693 do_l = do_list
581
694
582 def getsourcelines(self, obj):
695 def getsourcelines(self, obj):
583 lines, lineno = inspect.findsource(obj)
696 lines, lineno = inspect.findsource(obj)
584 if inspect.isframe(obj) and obj.f_globals is obj.f_locals:
697 if inspect.isframe(obj) and obj.f_globals is self._get_frame_locals(obj):
585 # must be a module frame: do not try to cut a block out of it
698 # must be a module frame: do not try to cut a block out of it
586 return lines, 1
699 return lines, 1
587 elif inspect.ismodule(obj):
700 elif inspect.ismodule(obj):
588 return lines, 1
701 return lines, 1
589 return inspect.getblock(lines[lineno:]), lineno+1
702 return inspect.getblock(lines[lineno:]), lineno+1
590
703
591 def do_longlist(self, arg):
704 def do_longlist(self, arg):
592 """Print lines of code from the current stack frame.
705 """Print lines of code from the current stack frame.
593
706
594 Shows more lines than 'list' does.
707 Shows more lines than 'list' does.
595 """
708 """
596 self.lastcmd = 'longlist'
709 self.lastcmd = 'longlist'
597 try:
710 try:
598 lines, lineno = self.getsourcelines(self.curframe)
711 lines, lineno = self.getsourcelines(self.curframe)
599 except OSError as err:
712 except OSError as err:
600 self.error(err)
713 self.error(err)
601 return
714 return
602 last = lineno + len(lines)
715 last = lineno + len(lines)
603 self.print_list_lines(self.curframe.f_code.co_filename, lineno, last)
716 self.print_list_lines(self.curframe.f_code.co_filename, lineno, last)
604 do_ll = do_longlist
717 do_ll = do_longlist
605
718
606 def do_debug(self, arg):
719 def do_debug(self, arg):
607 """debug code
720 """debug code
608 Enter a recursive debugger that steps through the code
721 Enter a recursive debugger that steps through the code
609 argument (which is an arbitrary expression or statement to be
722 argument (which is an arbitrary expression or statement to be
610 executed in the current environment).
723 executed in the current environment).
611 """
724 """
612 trace_function = sys.gettrace()
725 trace_function = sys.gettrace()
613 sys.settrace(None)
726 sys.settrace(None)
614 globals = self.curframe.f_globals
727 globals = self.curframe.f_globals
615 locals = self.curframe_locals
728 locals = self.curframe_locals
616 p = self.__class__(completekey=self.completekey,
729 p = self.__class__(completekey=self.completekey,
617 stdin=self.stdin, stdout=self.stdout)
730 stdin=self.stdin, stdout=self.stdout)
618 p.use_rawinput = self.use_rawinput
731 p.use_rawinput = self.use_rawinput
619 p.prompt = "(%s) " % self.prompt.strip()
732 p.prompt = "(%s) " % self.prompt.strip()
620 self.message("ENTERING RECURSIVE DEBUGGER")
733 self.message("ENTERING RECURSIVE DEBUGGER")
621 sys.call_tracing(p.run, (arg, globals, locals))
734 sys.call_tracing(p.run, (arg, globals, locals))
622 self.message("LEAVING RECURSIVE DEBUGGER")
735 self.message("LEAVING RECURSIVE DEBUGGER")
623 sys.settrace(trace_function)
736 sys.settrace(trace_function)
624 self.lastcmd = p.lastcmd
737 self.lastcmd = p.lastcmd
625
738
626 def do_pdef(self, arg):
739 def do_pdef(self, arg):
627 """Print the call signature for any callable object.
740 """Print the call signature for any callable object.
628
741
629 The debugger interface to %pdef"""
742 The debugger interface to %pdef"""
630 namespaces = [
743 namespaces = [
631 ("Locals", self.curframe_locals),
744 ("Locals", self.curframe_locals),
632 ("Globals", self.curframe.f_globals),
745 ("Globals", self.curframe.f_globals),
633 ]
746 ]
634 self.shell.find_line_magic("pdef")(arg, namespaces=namespaces)
747 self.shell.find_line_magic("pdef")(arg, namespaces=namespaces)
635
748
636 def do_pdoc(self, arg):
749 def do_pdoc(self, arg):
637 """Print the docstring for an object.
750 """Print the docstring for an object.
638
751
639 The debugger interface to %pdoc."""
752 The debugger interface to %pdoc."""
640 namespaces = [
753 namespaces = [
641 ("Locals", self.curframe_locals),
754 ("Locals", self.curframe_locals),
642 ("Globals", self.curframe.f_globals),
755 ("Globals", self.curframe.f_globals),
643 ]
756 ]
644 self.shell.find_line_magic("pdoc")(arg, namespaces=namespaces)
757 self.shell.find_line_magic("pdoc")(arg, namespaces=namespaces)
645
758
646 def do_pfile(self, arg):
759 def do_pfile(self, arg):
647 """Print (or run through pager) the file where an object is defined.
760 """Print (or run through pager) the file where an object is defined.
648
761
649 The debugger interface to %pfile.
762 The debugger interface to %pfile.
650 """
763 """
651 namespaces = [
764 namespaces = [
652 ("Locals", self.curframe_locals),
765 ("Locals", self.curframe_locals),
653 ("Globals", self.curframe.f_globals),
766 ("Globals", self.curframe.f_globals),
654 ]
767 ]
655 self.shell.find_line_magic("pfile")(arg, namespaces=namespaces)
768 self.shell.find_line_magic("pfile")(arg, namespaces=namespaces)
656
769
657 def do_pinfo(self, arg):
770 def do_pinfo(self, arg):
658 """Provide detailed information about an object.
771 """Provide detailed information about an object.
659
772
660 The debugger interface to %pinfo, i.e., obj?."""
773 The debugger interface to %pinfo, i.e., obj?."""
661 namespaces = [
774 namespaces = [
662 ("Locals", self.curframe_locals),
775 ("Locals", self.curframe_locals),
663 ("Globals", self.curframe.f_globals),
776 ("Globals", self.curframe.f_globals),
664 ]
777 ]
665 self.shell.find_line_magic("pinfo")(arg, namespaces=namespaces)
778 self.shell.find_line_magic("pinfo")(arg, namespaces=namespaces)
666
779
667 def do_pinfo2(self, arg):
780 def do_pinfo2(self, arg):
668 """Provide extra detailed information about an object.
781 """Provide extra detailed information about an object.
669
782
670 The debugger interface to %pinfo2, i.e., obj??."""
783 The debugger interface to %pinfo2, i.e., obj??."""
671 namespaces = [
784 namespaces = [
672 ("Locals", self.curframe_locals),
785 ("Locals", self.curframe_locals),
673 ("Globals", self.curframe.f_globals),
786 ("Globals", self.curframe.f_globals),
674 ]
787 ]
675 self.shell.find_line_magic("pinfo2")(arg, namespaces=namespaces)
788 self.shell.find_line_magic("pinfo2")(arg, namespaces=namespaces)
676
789
677 def do_psource(self, arg):
790 def do_psource(self, arg):
678 """Print (or run through pager) the source code for an object."""
791 """Print (or run through pager) the source code for an object."""
679 namespaces = [
792 namespaces = [
680 ("Locals", self.curframe_locals),
793 ("Locals", self.curframe_locals),
681 ("Globals", self.curframe.f_globals),
794 ("Globals", self.curframe.f_globals),
682 ]
795 ]
683 self.shell.find_line_magic("psource")(arg, namespaces=namespaces)
796 self.shell.find_line_magic("psource")(arg, namespaces=namespaces)
684
797
685 def do_where(self, arg):
798 def do_where(self, arg):
686 """w(here)
799 """w(here)
687 Print a stack trace, with the most recent frame at the bottom.
800 Print a stack trace, with the most recent frame at the bottom.
688 An arrow indicates the "current frame", which determines the
801 An arrow indicates the "current frame", which determines the
689 context of most commands. 'bt' is an alias for this command.
802 context of most commands. 'bt' is an alias for this command.
690
803
691 Take a number as argument as an (optional) number of context line to
804 Take a number as argument as an (optional) number of context line to
692 print"""
805 print"""
693 if arg:
806 if arg:
694 try:
807 try:
695 context = int(arg)
808 context = int(arg)
696 except ValueError as err:
809 except ValueError as err:
697 self.error(err)
810 self.error(err)
698 return
811 return
699 self.print_stack_trace(context)
812 self.print_stack_trace(context)
700 else:
813 else:
701 self.print_stack_trace()
814 self.print_stack_trace()
702
815
703 do_w = do_where
816 do_w = do_where
704
817
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:
710 Colors = self.color_scheme_table.active_colors
823 if self.report_skipped:
711 ColorsNormal = Colors.Normal
824 Colors = self.color_scheme_table.active_colors
712 print(f"{Colors.excName} [... skipped 1 hidden frame]{ColorsNormal}\n")
825 ColorsNormal = Colors.Normal
713
826 print(
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):
717 """u(p) [count]
832 """u(p) [count]
718 Move the current frame count (default one) levels up in the
833 Move the current frame count (default one) levels up in the
719 stack trace (to an older frame).
834 stack trace (to an older frame).
720
835
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
728 try:
843 try:
729 count = int(arg or 1)
844 count = int(arg or 1)
730 except ValueError:
845 except ValueError:
731 self.error("Invalid frame count (%s)" % arg)
846 self.error("Invalid frame count (%s)" % arg)
732 return
847 return
733 skipped = 0
848 skipped = 0
734 if count < 0:
849 if count < 0:
735 _newframe = 0
850 _newframe = 0
736 else:
851 else:
737 counter = 0
852 counter = 0
738 hidden_frames = self.hidden_frames(self.stack)
853 hidden_frames = self.hidden_frames(self.stack)
739 for i in range(self.curindex - 1, -1, -1):
854 for i in range(self.curindex - 1, -1, -1):
740 if hidden_frames[i] and self.skip_hidden:
855 if hidden_frames[i] and self.skip_hidden:
741 skipped += 1
856 skipped += 1
742 continue
857 continue
743 counter += 1
858 counter += 1
744 if counter >= count:
859 if counter >= count:
745 break
860 break
746 else:
861 else:
747 # if no break occured.
862 # if no break occured.
748 self.error(
863 self.error(
749 "all frames above hidden, use `skip_hidden False` to get get into those."
864 "all frames above hidden, use `skip_hidden False` to get get into those."
750 )
865 )
751 return
866 return
752
867
753 Colors = self.color_scheme_table.active_colors
868 Colors = self.color_scheme_table.active_colors
754 ColorsNormal = Colors.Normal
869 ColorsNormal = Colors.Normal
755 _newframe = i
870 _newframe = i
756 self._select_frame(_newframe)
871 self._select_frame(_newframe)
757 if skipped:
872 if skipped:
758 print(
873 print(
759 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
874 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
760 )
875 )
761
876
762 def do_down(self, arg):
877 def do_down(self, arg):
763 """d(own) [count]
878 """d(own) [count]
764 Move the current frame count (default one) levels down in the
879 Move the current frame count (default one) levels down in the
765 stack trace (to a newer frame).
880 stack trace (to a newer frame).
766
881
767 Will skip hidden frames.
882 Will skip hidden frames.
768 """
883 """
769 if self.curindex + 1 == len(self.stack):
884 if self.curindex + 1 == len(self.stack):
770 self.error("Newest frame")
885 self.error("Newest frame")
771 return
886 return
772 try:
887 try:
773 count = int(arg or 1)
888 count = int(arg or 1)
774 except ValueError:
889 except ValueError:
775 self.error("Invalid frame count (%s)" % arg)
890 self.error("Invalid frame count (%s)" % arg)
776 return
891 return
777 if count < 0:
892 if count < 0:
778 _newframe = len(self.stack) - 1
893 _newframe = len(self.stack) - 1
779 else:
894 else:
780 counter = 0
895 counter = 0
781 skipped = 0
896 skipped = 0
782 hidden_frames = self.hidden_frames(self.stack)
897 hidden_frames = self.hidden_frames(self.stack)
783 for i in range(self.curindex + 1, len(self.stack)):
898 for i in range(self.curindex + 1, len(self.stack)):
784 if hidden_frames[i] and self.skip_hidden:
899 if hidden_frames[i] and self.skip_hidden:
785 skipped += 1
900 skipped += 1
786 continue
901 continue
787 counter += 1
902 counter += 1
788 if counter >= count:
903 if counter >= count:
789 break
904 break
790 else:
905 else:
791 self.error(
906 self.error(
792 "all frames bellow hidden, use `skip_hidden False` to get get into those."
907 "all frames bellow hidden, use `skip_hidden False` to get get into those."
793 )
908 )
794 return
909 return
795
910
796 Colors = self.color_scheme_table.active_colors
911 Colors = self.color_scheme_table.active_colors
797 ColorsNormal = Colors.Normal
912 ColorsNormal = Colors.Normal
798 if skipped:
913 if skipped:
799 print(
914 print(
800 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
915 f"{Colors.excName} [... skipped {skipped} hidden frame(s)]{ColorsNormal}\n"
801 )
916 )
802 _newframe = i
917 _newframe = i
803
918
804 self._select_frame(_newframe)
919 self._select_frame(_newframe)
805
920
806 do_d = do_down
921 do_d = do_down
807 do_u = do_up
922 do_u = do_up
808
923
809 def do_context(self, context):
924 def do_context(self, context):
810 """context number_of_lines
925 """context number_of_lines
811 Set the number of lines of source code to show when displaying
926 Set the number of lines of source code to show when displaying
812 stacktrace information.
927 stacktrace information.
813 """
928 """
814 try:
929 try:
815 new_context = int(context)
930 new_context = int(context)
816 if new_context <= 0:
931 if new_context <= 0:
817 raise ValueError()
932 raise ValueError()
818 self.context = new_context
933 self.context = new_context
819 except ValueError:
934 except ValueError:
820 self.error("The 'context' command requires a positive integer argument.")
935 self.error("The 'context' command requires a positive integer argument.")
821
936
822
937
823 class InterruptiblePdb(Pdb):
938 class InterruptiblePdb(Pdb):
824 """Version of debugger where KeyboardInterrupt exits the debugger altogether."""
939 """Version of debugger where KeyboardInterrupt exits the debugger altogether."""
825
940
826 def cmdloop(self):
941 def cmdloop(self):
827 """Wrap cmdloop() such that KeyboardInterrupt stops the debugger."""
942 """Wrap cmdloop() such that KeyboardInterrupt stops the debugger."""
828 try:
943 try:
829 return OldPdb.cmdloop(self)
944 return OldPdb.cmdloop(self)
830 except KeyboardInterrupt:
945 except KeyboardInterrupt:
831 self.stop_here = lambda frame: False
946 self.stop_here = lambda frame: False
832 self.do_quit("")
947 self.do_quit("")
833 sys.settrace(None)
948 sys.settrace(None)
834 self.quitting = False
949 self.quitting = False
835 raise
950 raise
836
951
837 def _cmdloop(self):
952 def _cmdloop(self):
838 while True:
953 while True:
839 try:
954 try:
840 # keyboard interrupts allow for an easy way to cancel
955 # keyboard interrupts allow for an easy way to cancel
841 # the current command, so allow them during interactive input
956 # the current command, so allow them during interactive input
842 self.allow_kbdint = True
957 self.allow_kbdint = True
843 self.cmdloop()
958 self.cmdloop()
844 self.allow_kbdint = False
959 self.allow_kbdint = False
845 break
960 break
846 except KeyboardInterrupt:
961 except KeyboardInterrupt:
847 self.message('--KeyboardInterrupt--')
962 self.message('--KeyboardInterrupt--')
848 raise
963 raise
849
964
850
965
851 def set_trace(frame=None):
966 def set_trace(frame=None):
852 """
967 """
853 Start debugging from `frame`.
968 Start debugging from `frame`.
854
969
855 If frame is not specified, debugging starts from caller's frame.
970 If frame is not specified, debugging starts from caller's frame.
856 """
971 """
857 Pdb().set_trace(frame or sys._getframe().f_back)
972 Pdb().set_trace(frame or sys._getframe().f_back)
@@ -1,1234 +1,1256 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Top-level display functions for displaying object in different formats."""
2 """Top-level display functions for displaying object in different formats."""
3
3
4 # Copyright (c) IPython Development Team.
4 # Copyright (c) IPython Development Team.
5 # Distributed under the terms of the Modified BSD License.
5 # Distributed under the terms of the Modified BSD License.
6
6
7
7
8 from binascii import b2a_base64, hexlify
8 from binascii import b2a_base64, hexlify
9 import html
9 import html
10 import json
10 import json
11 import mimetypes
11 import mimetypes
12 import os
12 import os
13 import struct
13 import struct
14 import warnings
14 import warnings
15 from copy import deepcopy
15 from copy import deepcopy
16 from os.path import splitext
16 from os.path import splitext
17 from pathlib import Path, PurePath
17 from pathlib import Path, PurePath
18
18
19 from IPython.utils.py3compat import cast_unicode
19 from IPython.utils.py3compat import cast_unicode
20 from IPython.testing.skipdoctest import skip_doctest
20 from IPython.testing.skipdoctest import skip_doctest
21 from . import display_functions
21 from . import display_functions
22
22
23
23
24 __all__ = ['display_pretty', 'display_html', 'display_markdown',
24 __all__ = ['display_pretty', 'display_html', 'display_markdown',
25 'display_svg', 'display_png', 'display_jpeg', 'display_latex', 'display_json',
25 'display_svg', 'display_png', 'display_jpeg', 'display_latex', 'display_json',
26 'display_javascript', 'display_pdf', 'DisplayObject', 'TextDisplayObject',
26 'display_javascript', 'display_pdf', 'DisplayObject', 'TextDisplayObject',
27 'Pretty', 'HTML', 'Markdown', 'Math', 'Latex', 'SVG', 'ProgressBar', 'JSON',
27 'Pretty', 'HTML', 'Markdown', 'Math', 'Latex', 'SVG', 'ProgressBar', 'JSON',
28 'GeoJSON', 'Javascript', 'Image', 'set_matplotlib_formats',
28 'GeoJSON', 'Javascript', 'Image', 'set_matplotlib_formats',
29 'set_matplotlib_close',
29 'set_matplotlib_close',
30 'Video']
30 'Video']
31
31
32 _deprecated_names = ["display", "clear_output", "publish_display_data", "update_display", "DisplayHandle"]
32 _deprecated_names = ["display", "clear_output", "publish_display_data", "update_display", "DisplayHandle"]
33
33
34 __all__ = __all__ + _deprecated_names
34 __all__ = __all__ + _deprecated_names
35
35
36
36
37 # ----- warn to import from IPython.display -----
37 # ----- warn to import from IPython.display -----
38
38
39 from warnings import warn
39 from warnings import warn
40
40
41
41
42 def __getattr__(name):
42 def __getattr__(name):
43 if name in _deprecated_names:
43 if name in _deprecated_names:
44 warn(f"Importing {name} from IPython.core.display is deprecated since IPython 7.14, please import from IPython display", DeprecationWarning, stacklevel=2)
44 warn(f"Importing {name} from IPython.core.display is deprecated since IPython 7.14, please import from IPython display", DeprecationWarning, stacklevel=2)
45 return getattr(display_functions, name)
45 return getattr(display_functions, name)
46
46
47 if name in globals().keys():
47 if name in globals().keys():
48 return globals()[name]
48 return globals()[name]
49 else:
49 else:
50 raise AttributeError(f"module {__name__} has no attribute {name}")
50 raise AttributeError(f"module {__name__} has no attribute {name}")
51
51
52
52
53 #-----------------------------------------------------------------------------
53 #-----------------------------------------------------------------------------
54 # utility functions
54 # utility functions
55 #-----------------------------------------------------------------------------
55 #-----------------------------------------------------------------------------
56
56
57 def _safe_exists(path):
57 def _safe_exists(path):
58 """Check path, but don't let exceptions raise"""
58 """Check path, but don't let exceptions raise"""
59 try:
59 try:
60 return os.path.exists(path)
60 return os.path.exists(path)
61 except Exception:
61 except Exception:
62 return False
62 return False
63
63
64
64
65 def _display_mimetype(mimetype, objs, raw=False, metadata=None):
65 def _display_mimetype(mimetype, objs, raw=False, metadata=None):
66 """internal implementation of all display_foo methods
66 """internal implementation of all display_foo methods
67
67
68 Parameters
68 Parameters
69 ----------
69 ----------
70 mimetype : str
70 mimetype : str
71 The mimetype to be published (e.g. 'image/png')
71 The mimetype to be published (e.g. 'image/png')
72 *objs : object
72 *objs : object
73 The Python objects to display, or if raw=True raw text data to
73 The Python objects to display, or if raw=True raw text data to
74 display.
74 display.
75 raw : bool
75 raw : bool
76 Are the data objects raw data or Python objects that need to be
76 Are the data objects raw data or Python objects that need to be
77 formatted before display? [default: False]
77 formatted before display? [default: False]
78 metadata : dict (optional)
78 metadata : dict (optional)
79 Metadata to be associated with the specific mimetype output.
79 Metadata to be associated with the specific mimetype output.
80 """
80 """
81 if metadata:
81 if metadata:
82 metadata = {mimetype: metadata}
82 metadata = {mimetype: metadata}
83 if raw:
83 if raw:
84 # turn list of pngdata into list of { 'image/png': pngdata }
84 # turn list of pngdata into list of { 'image/png': pngdata }
85 objs = [ {mimetype: obj} for obj in objs ]
85 objs = [ {mimetype: obj} for obj in objs ]
86 display(*objs, raw=raw, metadata=metadata, include=[mimetype])
86 display(*objs, raw=raw, metadata=metadata, include=[mimetype])
87
87
88 #-----------------------------------------------------------------------------
88 #-----------------------------------------------------------------------------
89 # Main functions
89 # Main functions
90 #-----------------------------------------------------------------------------
90 #-----------------------------------------------------------------------------
91
91
92
92
93 def display_pretty(*objs, **kwargs):
93 def display_pretty(*objs, **kwargs):
94 """Display the pretty (default) representation of an object.
94 """Display the pretty (default) representation of an object.
95
95
96 Parameters
96 Parameters
97 ----------
97 ----------
98 *objs : object
98 *objs : object
99 The Python objects to display, or if raw=True raw text data to
99 The Python objects to display, or if raw=True raw text data to
100 display.
100 display.
101 raw : bool
101 raw : bool
102 Are the data objects raw data or Python objects that need to be
102 Are the data objects raw data or Python objects that need to be
103 formatted before display? [default: False]
103 formatted before display? [default: False]
104 metadata : dict (optional)
104 metadata : dict (optional)
105 Metadata to be associated with the specific mimetype output.
105 Metadata to be associated with the specific mimetype output.
106 """
106 """
107 _display_mimetype('text/plain', objs, **kwargs)
107 _display_mimetype('text/plain', objs, **kwargs)
108
108
109
109
110 def display_html(*objs, **kwargs):
110 def display_html(*objs, **kwargs):
111 """Display the HTML representation of an object.
111 """Display the HTML representation of an object.
112
112
113 Note: If raw=False and the object does not have a HTML
113 Note: If raw=False and the object does not have a HTML
114 representation, no HTML will be shown.
114 representation, no HTML will be shown.
115
115
116 Parameters
116 Parameters
117 ----------
117 ----------
118 *objs : object
118 *objs : object
119 The Python objects to display, or if raw=True raw HTML data to
119 The Python objects to display, or if raw=True raw HTML data to
120 display.
120 display.
121 raw : bool
121 raw : bool
122 Are the data objects raw data or Python objects that need to be
122 Are the data objects raw data or Python objects that need to be
123 formatted before display? [default: False]
123 formatted before display? [default: False]
124 metadata : dict (optional)
124 metadata : dict (optional)
125 Metadata to be associated with the specific mimetype output.
125 Metadata to be associated with the specific mimetype output.
126 """
126 """
127 _display_mimetype('text/html', objs, **kwargs)
127 _display_mimetype('text/html', objs, **kwargs)
128
128
129
129
130 def display_markdown(*objs, **kwargs):
130 def display_markdown(*objs, **kwargs):
131 """Displays the Markdown representation of an object.
131 """Displays the Markdown representation of an object.
132
132
133 Parameters
133 Parameters
134 ----------
134 ----------
135 *objs : object
135 *objs : object
136 The Python objects to display, or if raw=True raw markdown data to
136 The Python objects to display, or if raw=True raw markdown data to
137 display.
137 display.
138 raw : bool
138 raw : bool
139 Are the data objects raw data or Python objects that need to be
139 Are the data objects raw data or Python objects that need to be
140 formatted before display? [default: False]
140 formatted before display? [default: False]
141 metadata : dict (optional)
141 metadata : dict (optional)
142 Metadata to be associated with the specific mimetype output.
142 Metadata to be associated with the specific mimetype output.
143 """
143 """
144
144
145 _display_mimetype('text/markdown', objs, **kwargs)
145 _display_mimetype('text/markdown', objs, **kwargs)
146
146
147
147
148 def display_svg(*objs, **kwargs):
148 def display_svg(*objs, **kwargs):
149 """Display the SVG representation of an object.
149 """Display the SVG representation of an object.
150
150
151 Parameters
151 Parameters
152 ----------
152 ----------
153 *objs : object
153 *objs : object
154 The Python objects to display, or if raw=True raw svg data to
154 The Python objects to display, or if raw=True raw svg data to
155 display.
155 display.
156 raw : bool
156 raw : bool
157 Are the data objects raw data or Python objects that need to be
157 Are the data objects raw data or Python objects that need to be
158 formatted before display? [default: False]
158 formatted before display? [default: False]
159 metadata : dict (optional)
159 metadata : dict (optional)
160 Metadata to be associated with the specific mimetype output.
160 Metadata to be associated with the specific mimetype output.
161 """
161 """
162 _display_mimetype('image/svg+xml', objs, **kwargs)
162 _display_mimetype('image/svg+xml', objs, **kwargs)
163
163
164
164
165 def display_png(*objs, **kwargs):
165 def display_png(*objs, **kwargs):
166 """Display the PNG representation of an object.
166 """Display the PNG representation of an object.
167
167
168 Parameters
168 Parameters
169 ----------
169 ----------
170 *objs : object
170 *objs : object
171 The Python objects to display, or if raw=True raw png data to
171 The Python objects to display, or if raw=True raw png data to
172 display.
172 display.
173 raw : bool
173 raw : bool
174 Are the data objects raw data or Python objects that need to be
174 Are the data objects raw data or Python objects that need to be
175 formatted before display? [default: False]
175 formatted before display? [default: False]
176 metadata : dict (optional)
176 metadata : dict (optional)
177 Metadata to be associated with the specific mimetype output.
177 Metadata to be associated with the specific mimetype output.
178 """
178 """
179 _display_mimetype('image/png', objs, **kwargs)
179 _display_mimetype('image/png', objs, **kwargs)
180
180
181
181
182 def display_jpeg(*objs, **kwargs):
182 def display_jpeg(*objs, **kwargs):
183 """Display the JPEG representation of an object.
183 """Display the JPEG representation of an object.
184
184
185 Parameters
185 Parameters
186 ----------
186 ----------
187 *objs : object
187 *objs : object
188 The Python objects to display, or if raw=True raw JPEG data to
188 The Python objects to display, or if raw=True raw JPEG data to
189 display.
189 display.
190 raw : bool
190 raw : bool
191 Are the data objects raw data or Python objects that need to be
191 Are the data objects raw data or Python objects that need to be
192 formatted before display? [default: False]
192 formatted before display? [default: False]
193 metadata : dict (optional)
193 metadata : dict (optional)
194 Metadata to be associated with the specific mimetype output.
194 Metadata to be associated with the specific mimetype output.
195 """
195 """
196 _display_mimetype('image/jpeg', objs, **kwargs)
196 _display_mimetype('image/jpeg', objs, **kwargs)
197
197
198
198
199 def display_latex(*objs, **kwargs):
199 def display_latex(*objs, **kwargs):
200 """Display the LaTeX representation of an object.
200 """Display the LaTeX representation of an object.
201
201
202 Parameters
202 Parameters
203 ----------
203 ----------
204 *objs : object
204 *objs : object
205 The Python objects to display, or if raw=True raw latex data to
205 The Python objects to display, or if raw=True raw latex data to
206 display.
206 display.
207 raw : bool
207 raw : bool
208 Are the data objects raw data or Python objects that need to be
208 Are the data objects raw data or Python objects that need to be
209 formatted before display? [default: False]
209 formatted before display? [default: False]
210 metadata : dict (optional)
210 metadata : dict (optional)
211 Metadata to be associated with the specific mimetype output.
211 Metadata to be associated with the specific mimetype output.
212 """
212 """
213 _display_mimetype('text/latex', objs, **kwargs)
213 _display_mimetype('text/latex', objs, **kwargs)
214
214
215
215
216 def display_json(*objs, **kwargs):
216 def display_json(*objs, **kwargs):
217 """Display the JSON representation of an object.
217 """Display the JSON representation of an object.
218
218
219 Note that not many frontends support displaying JSON.
219 Note that not many frontends support displaying JSON.
220
220
221 Parameters
221 Parameters
222 ----------
222 ----------
223 *objs : object
223 *objs : object
224 The Python objects to display, or if raw=True raw json data to
224 The Python objects to display, or if raw=True raw json data to
225 display.
225 display.
226 raw : bool
226 raw : bool
227 Are the data objects raw data or Python objects that need to be
227 Are the data objects raw data or Python objects that need to be
228 formatted before display? [default: False]
228 formatted before display? [default: False]
229 metadata : dict (optional)
229 metadata : dict (optional)
230 Metadata to be associated with the specific mimetype output.
230 Metadata to be associated with the specific mimetype output.
231 """
231 """
232 _display_mimetype('application/json', objs, **kwargs)
232 _display_mimetype('application/json', objs, **kwargs)
233
233
234
234
235 def display_javascript(*objs, **kwargs):
235 def display_javascript(*objs, **kwargs):
236 """Display the Javascript representation of an object.
236 """Display the Javascript representation of an object.
237
237
238 Parameters
238 Parameters
239 ----------
239 ----------
240 *objs : object
240 *objs : object
241 The Python objects to display, or if raw=True raw javascript data to
241 The Python objects to display, or if raw=True raw javascript data to
242 display.
242 display.
243 raw : bool
243 raw : bool
244 Are the data objects raw data or Python objects that need to be
244 Are the data objects raw data or Python objects that need to be
245 formatted before display? [default: False]
245 formatted before display? [default: False]
246 metadata : dict (optional)
246 metadata : dict (optional)
247 Metadata to be associated with the specific mimetype output.
247 Metadata to be associated with the specific mimetype output.
248 """
248 """
249 _display_mimetype('application/javascript', objs, **kwargs)
249 _display_mimetype('application/javascript', objs, **kwargs)
250
250
251
251
252 def display_pdf(*objs, **kwargs):
252 def display_pdf(*objs, **kwargs):
253 """Display the PDF representation of an object.
253 """Display the PDF representation of an object.
254
254
255 Parameters
255 Parameters
256 ----------
256 ----------
257 *objs : object
257 *objs : object
258 The Python objects to display, or if raw=True raw javascript data to
258 The Python objects to display, or if raw=True raw javascript data to
259 display.
259 display.
260 raw : bool
260 raw : bool
261 Are the data objects raw data or Python objects that need to be
261 Are the data objects raw data or Python objects that need to be
262 formatted before display? [default: False]
262 formatted before display? [default: False]
263 metadata : dict (optional)
263 metadata : dict (optional)
264 Metadata to be associated with the specific mimetype output.
264 Metadata to be associated with the specific mimetype output.
265 """
265 """
266 _display_mimetype('application/pdf', objs, **kwargs)
266 _display_mimetype('application/pdf', objs, **kwargs)
267
267
268
268
269 #-----------------------------------------------------------------------------
269 #-----------------------------------------------------------------------------
270 # Smart classes
270 # Smart classes
271 #-----------------------------------------------------------------------------
271 #-----------------------------------------------------------------------------
272
272
273
273
274 class DisplayObject(object):
274 class DisplayObject(object):
275 """An object that wraps data to be displayed."""
275 """An object that wraps data to be displayed."""
276
276
277 _read_flags = 'r'
277 _read_flags = 'r'
278 _show_mem_addr = False
278 _show_mem_addr = False
279 metadata = None
279 metadata = None
280
280
281 def __init__(self, data=None, url=None, filename=None, metadata=None):
281 def __init__(self, data=None, url=None, filename=None, metadata=None):
282 """Create a display object given raw data.
282 """Create a display object given raw data.
283
283
284 When this object is returned by an expression or passed to the
284 When this object is returned by an expression or passed to the
285 display function, it will result in the data being displayed
285 display function, it will result in the data being displayed
286 in the frontend. The MIME type of the data should match the
286 in the frontend. The MIME type of the data should match the
287 subclasses used, so the Png subclass should be used for 'image/png'
287 subclasses used, so the Png subclass should be used for 'image/png'
288 data. If the data is a URL, the data will first be downloaded
288 data. If the data is a URL, the data will first be downloaded
289 and then displayed. If
289 and then displayed. If
290
290
291 Parameters
291 Parameters
292 ----------
292 ----------
293 data : unicode, str or bytes
293 data : unicode, str or bytes
294 The raw data or a URL or file to load the data from
294 The raw data or a URL or file to load the data from
295 url : unicode
295 url : unicode
296 A URL to download the data from.
296 A URL to download the data from.
297 filename : unicode
297 filename : unicode
298 Path to a local file to load the data from.
298 Path to a local file to load the data from.
299 metadata : dict
299 metadata : dict
300 Dict of metadata associated to be the object when displayed
300 Dict of metadata associated to be the object when displayed
301 """
301 """
302 if isinstance(data, (Path, PurePath)):
302 if isinstance(data, (Path, PurePath)):
303 data = str(data)
303 data = str(data)
304
304
305 if data is not None and isinstance(data, str):
305 if data is not None and isinstance(data, str):
306 if data.startswith('http') and url is None:
306 if data.startswith('http') and url is None:
307 url = data
307 url = data
308 filename = None
308 filename = None
309 data = None
309 data = None
310 elif _safe_exists(data) and filename is None:
310 elif _safe_exists(data) and filename is None:
311 url = None
311 url = None
312 filename = data
312 filename = data
313 data = None
313 data = None
314
314
315 self.url = url
315 self.url = url
316 self.filename = filename
316 self.filename = filename
317 # because of @data.setter methods in
317 # because of @data.setter methods in
318 # subclasses ensure url and filename are set
318 # subclasses ensure url and filename are set
319 # before assigning to self.data
319 # before assigning to self.data
320 self.data = data
320 self.data = data
321
321
322 if metadata is not None:
322 if metadata is not None:
323 self.metadata = metadata
323 self.metadata = metadata
324 elif self.metadata is None:
324 elif self.metadata is None:
325 self.metadata = {}
325 self.metadata = {}
326
326
327 self.reload()
327 self.reload()
328 self._check_data()
328 self._check_data()
329
329
330 def __repr__(self):
330 def __repr__(self):
331 if not self._show_mem_addr:
331 if not self._show_mem_addr:
332 cls = self.__class__
332 cls = self.__class__
333 r = "<%s.%s object>" % (cls.__module__, cls.__name__)
333 r = "<%s.%s object>" % (cls.__module__, cls.__name__)
334 else:
334 else:
335 r = super(DisplayObject, self).__repr__()
335 r = super(DisplayObject, self).__repr__()
336 return r
336 return r
337
337
338 def _check_data(self):
338 def _check_data(self):
339 """Override in subclasses if there's something to check."""
339 """Override in subclasses if there's something to check."""
340 pass
340 pass
341
341
342 def _data_and_metadata(self):
342 def _data_and_metadata(self):
343 """shortcut for returning metadata with shape information, if defined"""
343 """shortcut for returning metadata with shape information, if defined"""
344 if self.metadata:
344 if self.metadata:
345 return self.data, deepcopy(self.metadata)
345 return self.data, deepcopy(self.metadata)
346 else:
346 else:
347 return self.data
347 return self.data
348
348
349 def reload(self):
349 def reload(self):
350 """Reload the raw data from file or URL."""
350 """Reload the raw data from file or URL."""
351 if self.filename is not None:
351 if self.filename is not None:
352 with open(self.filename, self._read_flags) as f:
352 with open(self.filename, self._read_flags) as f:
353 self.data = f.read()
353 self.data = f.read()
354 elif self.url is not None:
354 elif self.url is not None:
355 # Deferred import
355 # Deferred import
356 from urllib.request import urlopen
356 from urllib.request import urlopen
357 response = urlopen(self.url)
357 response = urlopen(self.url)
358 data = response.read()
358 data = response.read()
359 # extract encoding from header, if there is one:
359 # extract encoding from header, if there is one:
360 encoding = None
360 encoding = None
361 if 'content-type' in response.headers:
361 if 'content-type' in response.headers:
362 for sub in response.headers['content-type'].split(';'):
362 for sub in response.headers['content-type'].split(';'):
363 sub = sub.strip()
363 sub = sub.strip()
364 if sub.startswith('charset'):
364 if sub.startswith('charset'):
365 encoding = sub.split('=')[-1].strip()
365 encoding = sub.split('=')[-1].strip()
366 break
366 break
367 if 'content-encoding' in response.headers:
367 if 'content-encoding' in response.headers:
368 # TODO: do deflate?
368 # TODO: do deflate?
369 if 'gzip' in response.headers['content-encoding']:
369 if 'gzip' in response.headers['content-encoding']:
370 import gzip
370 import gzip
371 from io import BytesIO
371 from io import BytesIO
372 with gzip.open(BytesIO(data), 'rt', encoding=encoding) as fp:
372 with gzip.open(BytesIO(data), 'rt', encoding=encoding) as fp:
373 encoding = None
373 encoding = None
374 data = fp.read()
374 data = fp.read()
375
375
376 # decode data, if an encoding was specified
376 # decode data, if an encoding was specified
377 # We only touch self.data once since
377 # We only touch self.data once since
378 # subclasses such as SVG have @data.setter methods
378 # subclasses such as SVG have @data.setter methods
379 # that transform self.data into ... well svg.
379 # that transform self.data into ... well svg.
380 if encoding:
380 if encoding:
381 self.data = data.decode(encoding, 'replace')
381 self.data = data.decode(encoding, 'replace')
382 else:
382 else:
383 self.data = data
383 self.data = data
384
384
385
385
386 class TextDisplayObject(DisplayObject):
386 class TextDisplayObject(DisplayObject):
387 """Validate that display data is text"""
387 """Validate that display data is text"""
388 def _check_data(self):
388 def _check_data(self):
389 if self.data is not None and not isinstance(self.data, str):
389 if self.data is not None and not isinstance(self.data, str):
390 raise TypeError("%s expects text, not %r" % (self.__class__.__name__, self.data))
390 raise TypeError("%s expects text, not %r" % (self.__class__.__name__, self.data))
391
391
392 class Pretty(TextDisplayObject):
392 class Pretty(TextDisplayObject):
393
393
394 def _repr_pretty_(self, pp, cycle):
394 def _repr_pretty_(self, pp, cycle):
395 return pp.text(self.data)
395 return pp.text(self.data)
396
396
397
397
398 class HTML(TextDisplayObject):
398 class HTML(TextDisplayObject):
399
399
400 def __init__(self, data=None, url=None, filename=None, metadata=None):
400 def __init__(self, data=None, url=None, filename=None, metadata=None):
401 def warn():
401 def warn():
402 if not data:
402 if not data:
403 return False
403 return False
404
404
405 #
405 #
406 # Avoid calling lower() on the entire data, because it could be a
406 # Avoid calling lower() on the entire data, because it could be a
407 # long string and we're only interested in its beginning and end.
407 # long string and we're only interested in its beginning and end.
408 #
408 #
409 prefix = data[:10].lower()
409 prefix = data[:10].lower()
410 suffix = data[-10:].lower()
410 suffix = data[-10:].lower()
411 return prefix.startswith("<iframe ") and suffix.endswith("</iframe>")
411 return prefix.startswith("<iframe ") and suffix.endswith("</iframe>")
412
412
413 if warn():
413 if warn():
414 warnings.warn("Consider using IPython.display.IFrame instead")
414 warnings.warn("Consider using IPython.display.IFrame instead")
415 super(HTML, self).__init__(data=data, url=url, filename=filename, metadata=metadata)
415 super(HTML, self).__init__(data=data, url=url, filename=filename, metadata=metadata)
416
416
417 def _repr_html_(self):
417 def _repr_html_(self):
418 return self._data_and_metadata()
418 return self._data_and_metadata()
419
419
420 def __html__(self):
420 def __html__(self):
421 """
421 """
422 This method exists to inform other HTML-using modules (e.g. Markupsafe,
422 This method exists to inform other HTML-using modules (e.g. Markupsafe,
423 htmltag, etc) that this object is HTML and does not need things like
423 htmltag, etc) that this object is HTML and does not need things like
424 special characters (<>&) escaped.
424 special characters (<>&) escaped.
425 """
425 """
426 return self._repr_html_()
426 return self._repr_html_()
427
427
428
428
429 class Markdown(TextDisplayObject):
429 class Markdown(TextDisplayObject):
430
430
431 def _repr_markdown_(self):
431 def _repr_markdown_(self):
432 return self._data_and_metadata()
432 return self._data_and_metadata()
433
433
434
434
435 class Math(TextDisplayObject):
435 class Math(TextDisplayObject):
436
436
437 def _repr_latex_(self):
437 def _repr_latex_(self):
438 s = r"$\displaystyle %s$" % self.data.strip('$')
438 s = r"$\displaystyle %s$" % self.data.strip('$')
439 if self.metadata:
439 if self.metadata:
440 return s, deepcopy(self.metadata)
440 return s, deepcopy(self.metadata)
441 else:
441 else:
442 return s
442 return s
443
443
444
444
445 class Latex(TextDisplayObject):
445 class Latex(TextDisplayObject):
446
446
447 def _repr_latex_(self):
447 def _repr_latex_(self):
448 return self._data_and_metadata()
448 return self._data_and_metadata()
449
449
450
450
451 class SVG(DisplayObject):
451 class SVG(DisplayObject):
452 """Embed an SVG into the display.
452 """Embed an SVG into the display.
453
453
454 Note if you just want to view a svg image via a URL use `:class:Image` with
454 Note if you just want to view a svg image via a URL use `:class:Image` with
455 a url=URL keyword argument.
455 a url=URL keyword argument.
456 """
456 """
457
457
458 _read_flags = 'rb'
458 _read_flags = 'rb'
459 # wrap data in a property, which extracts the <svg> tag, discarding
459 # wrap data in a property, which extracts the <svg> tag, discarding
460 # document headers
460 # document headers
461 _data = None
461 _data = None
462
462
463 @property
463 @property
464 def data(self):
464 def data(self):
465 return self._data
465 return self._data
466
466
467 @data.setter
467 @data.setter
468 def data(self, svg):
468 def data(self, svg):
469 if svg is None:
469 if svg is None:
470 self._data = None
470 self._data = None
471 return
471 return
472 # parse into dom object
472 # parse into dom object
473 from xml.dom import minidom
473 from xml.dom import minidom
474 x = minidom.parseString(svg)
474 x = minidom.parseString(svg)
475 # get svg tag (should be 1)
475 # get svg tag (should be 1)
476 found_svg = x.getElementsByTagName('svg')
476 found_svg = x.getElementsByTagName('svg')
477 if found_svg:
477 if found_svg:
478 svg = found_svg[0].toxml()
478 svg = found_svg[0].toxml()
479 else:
479 else:
480 # fallback on the input, trust the user
480 # fallback on the input, trust the user
481 # but this is probably an error.
481 # but this is probably an error.
482 pass
482 pass
483 svg = cast_unicode(svg)
483 svg = cast_unicode(svg)
484 self._data = svg
484 self._data = svg
485
485
486 def _repr_svg_(self):
486 def _repr_svg_(self):
487 return self._data_and_metadata()
487 return self._data_and_metadata()
488
488
489 class ProgressBar(DisplayObject):
489 class ProgressBar(DisplayObject):
490 """Progressbar supports displaying a progressbar like element
490 """Progressbar supports displaying a progressbar like element
491 """
491 """
492 def __init__(self, total):
492 def __init__(self, total):
493 """Creates a new progressbar
493 """Creates a new progressbar
494
494
495 Parameters
495 Parameters
496 ----------
496 ----------
497 total : int
497 total : int
498 maximum size of the progressbar
498 maximum size of the progressbar
499 """
499 """
500 self.total = total
500 self.total = total
501 self._progress = 0
501 self._progress = 0
502 self.html_width = '60ex'
502 self.html_width = '60ex'
503 self.text_width = 60
503 self.text_width = 60
504 self._display_id = hexlify(os.urandom(8)).decode('ascii')
504 self._display_id = hexlify(os.urandom(8)).decode('ascii')
505
505
506 def __repr__(self):
506 def __repr__(self):
507 fraction = self.progress / self.total
507 fraction = self.progress / self.total
508 filled = '=' * int(fraction * self.text_width)
508 filled = '=' * int(fraction * self.text_width)
509 rest = ' ' * (self.text_width - len(filled))
509 rest = ' ' * (self.text_width - len(filled))
510 return '[{}{}] {}/{}'.format(
510 return '[{}{}] {}/{}'.format(
511 filled, rest,
511 filled, rest,
512 self.progress, self.total,
512 self.progress, self.total,
513 )
513 )
514
514
515 def _repr_html_(self):
515 def _repr_html_(self):
516 return "<progress style='width:{}' max='{}' value='{}'></progress>".format(
516 return "<progress style='width:{}' max='{}' value='{}'></progress>".format(
517 self.html_width, self.total, self.progress)
517 self.html_width, self.total, self.progress)
518
518
519 def display(self):
519 def display(self):
520 display(self, display_id=self._display_id)
520 display(self, display_id=self._display_id)
521
521
522 def update(self):
522 def update(self):
523 display(self, display_id=self._display_id, update=True)
523 display(self, display_id=self._display_id, update=True)
524
524
525 @property
525 @property
526 def progress(self):
526 def progress(self):
527 return self._progress
527 return self._progress
528
528
529 @progress.setter
529 @progress.setter
530 def progress(self, value):
530 def progress(self, value):
531 self._progress = value
531 self._progress = value
532 self.update()
532 self.update()
533
533
534 def __iter__(self):
534 def __iter__(self):
535 self.display()
535 self.display()
536 self._progress = -1 # First iteration is 0
536 self._progress = -1 # First iteration is 0
537 return self
537 return self
538
538
539 def __next__(self):
539 def __next__(self):
540 """Returns current value and increments display by one."""
540 """Returns current value and increments display by one."""
541 self.progress += 1
541 self.progress += 1
542 if self.progress < self.total:
542 if self.progress < self.total:
543 return self.progress
543 return self.progress
544 else:
544 else:
545 raise StopIteration()
545 raise StopIteration()
546
546
547 class JSON(DisplayObject):
547 class JSON(DisplayObject):
548 """JSON expects a JSON-able dict or list
548 """JSON expects a JSON-able dict or list
549
549
550 not an already-serialized JSON string.
550 not an already-serialized JSON string.
551
551
552 Scalar types (None, number, string) are not allowed, only dict or list containers.
552 Scalar types (None, number, string) are not allowed, only dict or list containers.
553 """
553 """
554 # wrap data in a property, which warns about passing already-serialized JSON
554 # wrap data in a property, which warns about passing already-serialized JSON
555 _data = None
555 _data = None
556 def __init__(self, data=None, url=None, filename=None, expanded=False, metadata=None, root='root', **kwargs):
556 def __init__(self, data=None, url=None, filename=None, expanded=False, metadata=None, root='root', **kwargs):
557 """Create a JSON display object given raw data.
557 """Create a JSON display object given raw data.
558
558
559 Parameters
559 Parameters
560 ----------
560 ----------
561 data : dict or list
561 data : dict or list
562 JSON data to display. Not an already-serialized JSON string.
562 JSON data to display. Not an already-serialized JSON string.
563 Scalar types (None, number, string) are not allowed, only dict
563 Scalar types (None, number, string) are not allowed, only dict
564 or list containers.
564 or list containers.
565 url : unicode
565 url : unicode
566 A URL to download the data from.
566 A URL to download the data from.
567 filename : unicode
567 filename : unicode
568 Path to a local file to load the data from.
568 Path to a local file to load the data from.
569 expanded : boolean
569 expanded : boolean
570 Metadata to control whether a JSON display component is expanded.
570 Metadata to control whether a JSON display component is expanded.
571 metadata : dict
571 metadata : dict
572 Specify extra metadata to attach to the json display object.
572 Specify extra metadata to attach to the json display object.
573 root : str
573 root : str
574 The name of the root element of the JSON tree
574 The name of the root element of the JSON tree
575 """
575 """
576 self.metadata = {
576 self.metadata = {
577 'expanded': expanded,
577 'expanded': expanded,
578 'root': root,
578 'root': root,
579 }
579 }
580 if metadata:
580 if metadata:
581 self.metadata.update(metadata)
581 self.metadata.update(metadata)
582 if kwargs:
582 if kwargs:
583 self.metadata.update(kwargs)
583 self.metadata.update(kwargs)
584 super(JSON, self).__init__(data=data, url=url, filename=filename)
584 super(JSON, self).__init__(data=data, url=url, filename=filename)
585
585
586 def _check_data(self):
586 def _check_data(self):
587 if self.data is not None and not isinstance(self.data, (dict, list)):
587 if self.data is not None and not isinstance(self.data, (dict, list)):
588 raise TypeError("%s expects JSONable dict or list, not %r" % (self.__class__.__name__, self.data))
588 raise TypeError("%s expects JSONable dict or list, not %r" % (self.__class__.__name__, self.data))
589
589
590 @property
590 @property
591 def data(self):
591 def data(self):
592 return self._data
592 return self._data
593
593
594 @data.setter
594 @data.setter
595 def data(self, data):
595 def data(self, data):
596 if isinstance(data, (Path, PurePath)):
596 if isinstance(data, (Path, PurePath)):
597 data = str(data)
597 data = str(data)
598
598
599 if isinstance(data, str):
599 if isinstance(data, str):
600 if self.filename is None and self.url is None:
600 if self.filename is None and self.url is None:
601 warnings.warn("JSON expects JSONable dict or list, not JSON strings")
601 warnings.warn("JSON expects JSONable dict or list, not JSON strings")
602 data = json.loads(data)
602 data = json.loads(data)
603 self._data = data
603 self._data = data
604
604
605 def _data_and_metadata(self):
605 def _data_and_metadata(self):
606 return self.data, self.metadata
606 return self.data, self.metadata
607
607
608 def _repr_json_(self):
608 def _repr_json_(self):
609 return self._data_and_metadata()
609 return self._data_and_metadata()
610
610
611 _css_t = """var link = document.createElement("link");
611 _css_t = """var link = document.createElement("link");
612 link.ref = "stylesheet";
612 link.ref = "stylesheet";
613 link.type = "text/css";
613 link.type = "text/css";
614 link.href = "%s";
614 link.href = "%s";
615 document.head.appendChild(link);
615 document.head.appendChild(link);
616 """
616 """
617
617
618 _lib_t1 = """new Promise(function(resolve, reject) {
618 _lib_t1 = """new Promise(function(resolve, reject) {
619 var script = document.createElement("script");
619 var script = document.createElement("script");
620 script.onload = resolve;
620 script.onload = resolve;
621 script.onerror = reject;
621 script.onerror = reject;
622 script.src = "%s";
622 script.src = "%s";
623 document.head.appendChild(script);
623 document.head.appendChild(script);
624 }).then(() => {
624 }).then(() => {
625 """
625 """
626
626
627 _lib_t2 = """
627 _lib_t2 = """
628 });"""
628 });"""
629
629
630 class GeoJSON(JSON):
630 class GeoJSON(JSON):
631 """GeoJSON expects JSON-able dict
631 """GeoJSON expects JSON-able dict
632
632
633 not an already-serialized JSON string.
633 not an already-serialized JSON string.
634
634
635 Scalar types (None, number, string) are not allowed, only dict containers.
635 Scalar types (None, number, string) are not allowed, only dict containers.
636 """
636 """
637
637
638 def __init__(self, *args, **kwargs):
638 def __init__(self, *args, **kwargs):
639 """Create a GeoJSON display object given raw data.
639 """Create a GeoJSON display object given raw data.
640
640
641 Parameters
641 Parameters
642 ----------
642 ----------
643 data : dict or list
643 data : dict or list
644 VegaLite data. Not an already-serialized JSON string.
644 VegaLite data. Not an already-serialized JSON string.
645 Scalar types (None, number, string) are not allowed, only dict
645 Scalar types (None, number, string) are not allowed, only dict
646 or list containers.
646 or list containers.
647 url_template : string
647 url_template : string
648 Leaflet TileLayer URL template: http://leafletjs.com/reference.html#url-template
648 Leaflet TileLayer URL template: http://leafletjs.com/reference.html#url-template
649 layer_options : dict
649 layer_options : dict
650 Leaflet TileLayer options: http://leafletjs.com/reference.html#tilelayer-options
650 Leaflet TileLayer options: http://leafletjs.com/reference.html#tilelayer-options
651 url : unicode
651 url : unicode
652 A URL to download the data from.
652 A URL to download the data from.
653 filename : unicode
653 filename : unicode
654 Path to a local file to load the data from.
654 Path to a local file to load the data from.
655 metadata : dict
655 metadata : dict
656 Specify extra metadata to attach to the json display object.
656 Specify extra metadata to attach to the json display object.
657
657
658 Examples
658 Examples
659 --------
659 --------
660 The following will display an interactive map of Mars with a point of
660 The following will display an interactive map of Mars with a point of
661 interest on frontend that do support GeoJSON display.
661 interest on frontend that do support GeoJSON display.
662
662
663 >>> from IPython.display import GeoJSON
663 >>> from IPython.display import GeoJSON
664
664
665 >>> GeoJSON(data={
665 >>> GeoJSON(data={
666 ... "type": "Feature",
666 ... "type": "Feature",
667 ... "geometry": {
667 ... "geometry": {
668 ... "type": "Point",
668 ... "type": "Point",
669 ... "coordinates": [-81.327, 296.038]
669 ... "coordinates": [-81.327, 296.038]
670 ... }
670 ... }
671 ... },
671 ... },
672 ... url_template="http://s3-eu-west-1.amazonaws.com/whereonmars.cartodb.net/{basemap_id}/{z}/{x}/{y}.png",
672 ... url_template="http://s3-eu-west-1.amazonaws.com/whereonmars.cartodb.net/{basemap_id}/{z}/{x}/{y}.png",
673 ... layer_options={
673 ... layer_options={
674 ... "basemap_id": "celestia_mars-shaded-16k_global",
674 ... "basemap_id": "celestia_mars-shaded-16k_global",
675 ... "attribution" : "Celestia/praesepe",
675 ... "attribution" : "Celestia/praesepe",
676 ... "minZoom" : 0,
676 ... "minZoom" : 0,
677 ... "maxZoom" : 18,
677 ... "maxZoom" : 18,
678 ... })
678 ... })
679 <IPython.core.display.GeoJSON object>
679 <IPython.core.display.GeoJSON object>
680
680
681 In the terminal IPython, you will only see the text representation of
681 In the terminal IPython, you will only see the text representation of
682 the GeoJSON object.
682 the GeoJSON object.
683
683
684 """
684 """
685
685
686 super(GeoJSON, self).__init__(*args, **kwargs)
686 super(GeoJSON, self).__init__(*args, **kwargs)
687
687
688
688
689 def _ipython_display_(self):
689 def _ipython_display_(self):
690 bundle = {
690 bundle = {
691 'application/geo+json': self.data,
691 'application/geo+json': self.data,
692 'text/plain': '<IPython.display.GeoJSON object>'
692 'text/plain': '<IPython.display.GeoJSON object>'
693 }
693 }
694 metadata = {
694 metadata = {
695 'application/geo+json': self.metadata
695 'application/geo+json': self.metadata
696 }
696 }
697 display(bundle, metadata=metadata, raw=True)
697 display(bundle, metadata=metadata, raw=True)
698
698
699 class Javascript(TextDisplayObject):
699 class Javascript(TextDisplayObject):
700
700
701 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
701 def __init__(self, data=None, url=None, filename=None, lib=None, css=None):
702 """Create a Javascript display object given raw data.
702 """Create a Javascript display object given raw data.
703
703
704 When this object is returned by an expression or passed to the
704 When this object is returned by an expression or passed to the
705 display function, it will result in the data being displayed
705 display function, it will result in the data being displayed
706 in the frontend. If the data is a URL, the data will first be
706 in the frontend. If the data is a URL, the data will first be
707 downloaded and then displayed.
707 downloaded and then displayed.
708
708
709 In the Notebook, the containing element will be available as `element`,
709 In the Notebook, the containing element will be available as `element`,
710 and jQuery will be available. Content appended to `element` will be
710 and jQuery will be available. Content appended to `element` will be
711 visible in the output area.
711 visible in the output area.
712
712
713 Parameters
713 Parameters
714 ----------
714 ----------
715 data : unicode, str or bytes
715 data : unicode, str or bytes
716 The Javascript source code or a URL to download it from.
716 The Javascript source code or a URL to download it from.
717 url : unicode
717 url : unicode
718 A URL to download the data from.
718 A URL to download the data from.
719 filename : unicode
719 filename : unicode
720 Path to a local file to load the data from.
720 Path to a local file to load the data from.
721 lib : list or str
721 lib : list or str
722 A sequence of Javascript library URLs to load asynchronously before
722 A sequence of Javascript library URLs to load asynchronously before
723 running the source code. The full URLs of the libraries should
723 running the source code. The full URLs of the libraries should
724 be given. A single Javascript library URL can also be given as a
724 be given. A single Javascript library URL can also be given as a
725 string.
725 string.
726 css : list or str
726 css : list or str
727 A sequence of css files to load before running the source code.
727 A sequence of css files to load before running the source code.
728 The full URLs of the css files should be given. A single css URL
728 The full URLs of the css files should be given. A single css URL
729 can also be given as a string.
729 can also be given as a string.
730 """
730 """
731 if isinstance(lib, str):
731 if isinstance(lib, str):
732 lib = [lib]
732 lib = [lib]
733 elif lib is None:
733 elif lib is None:
734 lib = []
734 lib = []
735 if isinstance(css, str):
735 if isinstance(css, str):
736 css = [css]
736 css = [css]
737 elif css is None:
737 elif css is None:
738 css = []
738 css = []
739 if not isinstance(lib, (list,tuple)):
739 if not isinstance(lib, (list,tuple)):
740 raise TypeError('expected sequence, got: %r' % lib)
740 raise TypeError('expected sequence, got: %r' % lib)
741 if not isinstance(css, (list,tuple)):
741 if not isinstance(css, (list,tuple)):
742 raise TypeError('expected sequence, got: %r' % css)
742 raise TypeError('expected sequence, got: %r' % css)
743 self.lib = lib
743 self.lib = lib
744 self.css = css
744 self.css = css
745 super(Javascript, self).__init__(data=data, url=url, filename=filename)
745 super(Javascript, self).__init__(data=data, url=url, filename=filename)
746
746
747 def _repr_javascript_(self):
747 def _repr_javascript_(self):
748 r = ''
748 r = ''
749 for c in self.css:
749 for c in self.css:
750 r += _css_t % c
750 r += _css_t % c
751 for l in self.lib:
751 for l in self.lib:
752 r += _lib_t1 % l
752 r += _lib_t1 % l
753 r += self.data
753 r += self.data
754 r += _lib_t2*len(self.lib)
754 r += _lib_t2*len(self.lib)
755 return r
755 return r
756
756
757 # constants for identifying png/jpeg data
757 # constants for identifying png/jpeg data
758 _PNG = b'\x89PNG\r\n\x1a\n'
758 _PNG = b'\x89PNG\r\n\x1a\n'
759 _JPEG = b'\xff\xd8'
759 _JPEG = b'\xff\xd8'
760
760
761 def _pngxy(data):
761 def _pngxy(data):
762 """read the (width, height) from a PNG header"""
762 """read the (width, height) from a PNG header"""
763 ihdr = data.index(b'IHDR')
763 ihdr = data.index(b'IHDR')
764 # next 8 bytes are width/height
764 # next 8 bytes are width/height
765 return struct.unpack('>ii', data[ihdr+4:ihdr+12])
765 return struct.unpack('>ii', data[ihdr+4:ihdr+12])
766
766
767 def _jpegxy(data):
767 def _jpegxy(data):
768 """read the (width, height) from a JPEG header"""
768 """read the (width, height) from a JPEG header"""
769 # adapted from http://www.64lines.com/jpeg-width-height
769 # adapted from http://www.64lines.com/jpeg-width-height
770
770
771 idx = 4
771 idx = 4
772 while True:
772 while True:
773 block_size = struct.unpack('>H', data[idx:idx+2])[0]
773 block_size = struct.unpack('>H', data[idx:idx+2])[0]
774 idx = idx + block_size
774 idx = idx + block_size
775 if data[idx:idx+2] == b'\xFF\xC0':
775 if data[idx:idx+2] == b'\xFF\xC0':
776 # found Start of Frame
776 # found Start of Frame
777 iSOF = idx
777 iSOF = idx
778 break
778 break
779 else:
779 else:
780 # read another block
780 # read another block
781 idx += 2
781 idx += 2
782
782
783 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
783 h, w = struct.unpack('>HH', data[iSOF+5:iSOF+9])
784 return w, h
784 return w, h
785
785
786 def _gifxy(data):
786 def _gifxy(data):
787 """read the (width, height) from a GIF header"""
787 """read the (width, height) from a GIF header"""
788 return struct.unpack('<HH', data[6:10])
788 return struct.unpack('<HH', data[6:10])
789
789
790
790
791 class Image(DisplayObject):
791 class Image(DisplayObject):
792
792
793 _read_flags = 'rb'
793 _read_flags = 'rb'
794 _FMT_JPEG = u'jpeg'
794 _FMT_JPEG = u'jpeg'
795 _FMT_PNG = u'png'
795 _FMT_PNG = u'png'
796 _FMT_GIF = u'gif'
796 _FMT_GIF = u'gif'
797 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG, _FMT_GIF]
797 _ACCEPTABLE_EMBEDDINGS = [_FMT_JPEG, _FMT_PNG, _FMT_GIF]
798 _MIMETYPES = {
798 _MIMETYPES = {
799 _FMT_PNG: 'image/png',
799 _FMT_PNG: 'image/png',
800 _FMT_JPEG: 'image/jpeg',
800 _FMT_JPEG: 'image/jpeg',
801 _FMT_GIF: 'image/gif',
801 _FMT_GIF: 'image/gif',
802 }
802 }
803
803
804 def __init__(
804 def __init__(
805 self,
805 self,
806 data=None,
806 data=None,
807 url=None,
807 url=None,
808 filename=None,
808 filename=None,
809 format=None,
809 format=None,
810 embed=None,
810 embed=None,
811 width=None,
811 width=None,
812 height=None,
812 height=None,
813 retina=False,
813 retina=False,
814 unconfined=False,
814 unconfined=False,
815 metadata=None,
815 metadata=None,
816 alt=None,
816 alt=None,
817 ):
817 ):
818 """Create a PNG/JPEG/GIF image object given raw data.
818 """Create a PNG/JPEG/GIF image object given raw data.
819
819
820 When this object is returned by an input cell or passed to the
820 When this object is returned by an input cell or passed to the
821 display function, it will result in the image being displayed
821 display function, it will result in the image being displayed
822 in the frontend.
822 in the frontend.
823
823
824 Parameters
824 Parameters
825 ----------
825 ----------
826 data : unicode, str or bytes
826 data : unicode, str or bytes
827 The raw image data or a URL or filename to load the data from.
827 The raw image data or a URL or filename to load the data from.
828 This always results in embedded image data.
828 This always results in embedded image data.
829 url : unicode
829 url : unicode
830 A URL to download the data from. If you specify `url=`,
830 A URL to download the data from. If you specify `url=`,
831 the image data will not be embedded unless you also specify `embed=True`.
831 the image data will not be embedded unless you also specify `embed=True`.
832 filename : unicode
832 filename : unicode
833 Path to a local file to load the data from.
833 Path to a local file to load the data from.
834 Images from a file are always embedded.
834 Images from a file are always embedded.
835 format : unicode
835 format : unicode
836 The format of the image data (png/jpeg/jpg/gif). If a filename or URL is given
836 The format of the image data (png/jpeg/jpg/gif). If a filename or URL is given
837 for format will be inferred from the filename extension.
837 for format will be inferred from the filename extension.
838 embed : bool
838 embed : bool
839 Should the image data be embedded using a data URI (True) or be
839 Should the image data be embedded using a data URI (True) or be
840 loaded using an <img> tag. Set this to True if you want the image
840 loaded using an <img> tag. Set this to True if you want the image
841 to be viewable later with no internet connection in the notebook.
841 to be viewable later with no internet connection in the notebook.
842
842
843 Default is `True`, unless the keyword argument `url` is set, then
843 Default is `True`, unless the keyword argument `url` is set, then
844 default value is `False`.
844 default value is `False`.
845
845
846 Note that QtConsole is not able to display images if `embed` is set to `False`
846 Note that QtConsole is not able to display images if `embed` is set to `False`
847 width : int
847 width : int
848 Width in pixels to which to constrain the image in html
848 Width in pixels to which to constrain the image in html
849 height : int
849 height : int
850 Height in pixels to which to constrain the image in html
850 Height in pixels to which to constrain the image in html
851 retina : bool
851 retina : bool
852 Automatically set the width and height to half of the measured
852 Automatically set the width and height to half of the measured
853 width and height.
853 width and height.
854 This only works for embedded images because it reads the width/height
854 This only works for embedded images because it reads the width/height
855 from image data.
855 from image data.
856 For non-embedded images, you can just set the desired display width
856 For non-embedded images, you can just set the desired display width
857 and height directly.
857 and height directly.
858 unconfined : bool
858 unconfined : bool
859 Set unconfined=True to disable max-width confinement of the image.
859 Set unconfined=True to disable max-width confinement of the image.
860 metadata : dict
860 metadata : dict
861 Specify extra metadata to attach to the image.
861 Specify extra metadata to attach to the image.
862 alt : unicode
862 alt : unicode
863 Alternative text for the image, for use by screen readers.
863 Alternative text for the image, for use by screen readers.
864
864
865 Examples
865 Examples
866 --------
866 --------
867 embedded image data, works in qtconsole and notebook
867 embedded image data, works in qtconsole and notebook
868 when passed positionally, the first arg can be any of raw image data,
868 when passed positionally, the first arg can be any of raw image data,
869 a URL, or a filename from which to load image data.
869 a URL, or a filename from which to load image data.
870 The result is always embedding image data for inline images.
870 The result is always embedding image data for inline images.
871
871
872 >>> Image('http://www.google.fr/images/srpr/logo3w.png')
872 >>> Image('http://www.google.fr/images/srpr/logo3w.png')
873 <IPython.core.display.Image object>
873 <IPython.core.display.Image object>
874
874
875 >>> Image('/path/to/image.jpg')
875 >>> Image('/path/to/image.jpg')
876 <IPython.core.display.Image object>
876 <IPython.core.display.Image object>
877
877
878 >>> Image(b'RAW_PNG_DATA...')
878 >>> Image(b'RAW_PNG_DATA...')
879 <IPython.core.display.Image object>
879 <IPython.core.display.Image object>
880
880
881 Specifying Image(url=...) does not embed the image data,
881 Specifying Image(url=...) does not embed the image data,
882 it only generates ``<img>`` tag with a link to the source.
882 it only generates ``<img>`` tag with a link to the source.
883 This will not work in the qtconsole or offline.
883 This will not work in the qtconsole or offline.
884
884
885 >>> Image(url='http://www.google.fr/images/srpr/logo3w.png')
885 >>> Image(url='http://www.google.fr/images/srpr/logo3w.png')
886 <IPython.core.display.Image object>
886 <IPython.core.display.Image object>
887
887
888 """
888 """
889 if isinstance(data, (Path, PurePath)):
889 if isinstance(data, (Path, PurePath)):
890 data = str(data)
890 data = str(data)
891
891
892 if filename is not None:
892 if filename is not None:
893 ext = self._find_ext(filename)
893 ext = self._find_ext(filename)
894 elif url is not None:
894 elif url is not None:
895 ext = self._find_ext(url)
895 ext = self._find_ext(url)
896 elif data is None:
896 elif data is None:
897 raise ValueError("No image data found. Expecting filename, url, or data.")
897 raise ValueError("No image data found. Expecting filename, url, or data.")
898 elif isinstance(data, str) and (
898 elif isinstance(data, str) and (
899 data.startswith('http') or _safe_exists(data)
899 data.startswith('http') or _safe_exists(data)
900 ):
900 ):
901 ext = self._find_ext(data)
901 ext = self._find_ext(data)
902 else:
902 else:
903 ext = None
903 ext = None
904
904
905 if format is None:
905 if format is None:
906 if ext is not None:
906 if ext is not None:
907 if ext == u'jpg' or ext == u'jpeg':
907 if ext == u'jpg' or ext == u'jpeg':
908 format = self._FMT_JPEG
908 format = self._FMT_JPEG
909 elif ext == u'png':
909 elif ext == u'png':
910 format = self._FMT_PNG
910 format = self._FMT_PNG
911 elif ext == u'gif':
911 elif ext == u'gif':
912 format = self._FMT_GIF
912 format = self._FMT_GIF
913 else:
913 else:
914 format = ext.lower()
914 format = ext.lower()
915 elif isinstance(data, bytes):
915 elif isinstance(data, bytes):
916 # infer image type from image data header,
916 # infer image type from image data header,
917 # only if format has not been specified.
917 # only if format has not been specified.
918 if data[:2] == _JPEG:
918 if data[:2] == _JPEG:
919 format = self._FMT_JPEG
919 format = self._FMT_JPEG
920
920
921 # failed to detect format, default png
921 # failed to detect format, default png
922 if format is None:
922 if format is None:
923 format = self._FMT_PNG
923 format = self._FMT_PNG
924
924
925 if format.lower() == 'jpg':
925 if format.lower() == 'jpg':
926 # jpg->jpeg
926 # jpg->jpeg
927 format = self._FMT_JPEG
927 format = self._FMT_JPEG
928
928
929 self.format = format.lower()
929 self.format = format.lower()
930 self.embed = embed if embed is not None else (url is None)
930 self.embed = embed if embed is not None else (url is None)
931
931
932 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
932 if self.embed and self.format not in self._ACCEPTABLE_EMBEDDINGS:
933 raise ValueError("Cannot embed the '%s' image format" % (self.format))
933 raise ValueError("Cannot embed the '%s' image format" % (self.format))
934 if self.embed:
934 if self.embed:
935 self._mimetype = self._MIMETYPES.get(self.format)
935 self._mimetype = self._MIMETYPES.get(self.format)
936
936
937 self.width = width
937 self.width = width
938 self.height = height
938 self.height = height
939 self.retina = retina
939 self.retina = retina
940 self.unconfined = unconfined
940 self.unconfined = unconfined
941 self.alt = alt
941 self.alt = alt
942 super(Image, self).__init__(data=data, url=url, filename=filename,
942 super(Image, self).__init__(data=data, url=url, filename=filename,
943 metadata=metadata)
943 metadata=metadata)
944
944
945 if self.width is None and self.metadata.get('width', {}):
945 if self.width is None and self.metadata.get('width', {}):
946 self.width = metadata['width']
946 self.width = metadata['width']
947
947
948 if self.height is None and self.metadata.get('height', {}):
948 if self.height is None and self.metadata.get('height', {}):
949 self.height = metadata['height']
949 self.height = metadata['height']
950
950
951 if self.alt is None and self.metadata.get("alt", {}):
951 if self.alt is None and self.metadata.get("alt", {}):
952 self.alt = metadata["alt"]
952 self.alt = metadata["alt"]
953
953
954 if retina:
954 if retina:
955 self._retina_shape()
955 self._retina_shape()
956
956
957
957
958 def _retina_shape(self):
958 def _retina_shape(self):
959 """load pixel-doubled width and height from image data"""
959 """load pixel-doubled width and height from image data"""
960 if not self.embed:
960 if not self.embed:
961 return
961 return
962 if self.format == self._FMT_PNG:
962 if self.format == self._FMT_PNG:
963 w, h = _pngxy(self.data)
963 w, h = _pngxy(self.data)
964 elif self.format == self._FMT_JPEG:
964 elif self.format == self._FMT_JPEG:
965 w, h = _jpegxy(self.data)
965 w, h = _jpegxy(self.data)
966 elif self.format == self._FMT_GIF:
966 elif self.format == self._FMT_GIF:
967 w, h = _gifxy(self.data)
967 w, h = _gifxy(self.data)
968 else:
968 else:
969 # retina only supports png
969 # retina only supports png
970 return
970 return
971 self.width = w // 2
971 self.width = w // 2
972 self.height = h // 2
972 self.height = h // 2
973
973
974 def reload(self):
974 def reload(self):
975 """Reload the raw data from file or URL."""
975 """Reload the raw data from file or URL."""
976 if self.embed:
976 if self.embed:
977 super(Image,self).reload()
977 super(Image,self).reload()
978 if self.retina:
978 if self.retina:
979 self._retina_shape()
979 self._retina_shape()
980
980
981 def _repr_html_(self):
981 def _repr_html_(self):
982 if not self.embed:
982 if not self.embed:
983 width = height = klass = alt = ""
983 width = height = klass = alt = ""
984 if self.width:
984 if self.width:
985 width = ' width="%d"' % self.width
985 width = ' width="%d"' % self.width
986 if self.height:
986 if self.height:
987 height = ' height="%d"' % self.height
987 height = ' height="%d"' % self.height
988 if self.unconfined:
988 if self.unconfined:
989 klass = ' class="unconfined"'
989 klass = ' class="unconfined"'
990 if self.alt:
990 if self.alt:
991 alt = ' alt="%s"' % html.escape(self.alt)
991 alt = ' alt="%s"' % html.escape(self.alt)
992 return '<img src="{url}"{width}{height}{klass}{alt}/>'.format(
992 return '<img src="{url}"{width}{height}{klass}{alt}/>'.format(
993 url=self.url,
993 url=self.url,
994 width=width,
994 width=width,
995 height=height,
995 height=height,
996 klass=klass,
996 klass=klass,
997 alt=alt,
997 alt=alt,
998 )
998 )
999
999
1000 def _repr_mimebundle_(self, include=None, exclude=None):
1000 def _repr_mimebundle_(self, include=None, exclude=None):
1001 """Return the image as a mimebundle
1001 """Return the image as a mimebundle
1002
1002
1003 Any new mimetype support should be implemented here.
1003 Any new mimetype support should be implemented here.
1004 """
1004 """
1005 if self.embed:
1005 if self.embed:
1006 mimetype = self._mimetype
1006 mimetype = self._mimetype
1007 data, metadata = self._data_and_metadata(always_both=True)
1007 data, metadata = self._data_and_metadata(always_both=True)
1008 if metadata:
1008 if metadata:
1009 metadata = {mimetype: metadata}
1009 metadata = {mimetype: metadata}
1010 return {mimetype: data}, metadata
1010 return {mimetype: data}, metadata
1011 else:
1011 else:
1012 return {'text/html': self._repr_html_()}
1012 return {'text/html': self._repr_html_()}
1013
1013
1014 def _data_and_metadata(self, always_both=False):
1014 def _data_and_metadata(self, always_both=False):
1015 """shortcut for returning metadata with shape information, if defined"""
1015 """shortcut for returning metadata with shape information, if defined"""
1016 try:
1016 try:
1017 b64_data = b2a_base64(self.data).decode('ascii')
1017 b64_data = b2a_base64(self.data).decode('ascii')
1018 except TypeError as e:
1018 except TypeError as e:
1019 raise FileNotFoundError(
1019 raise FileNotFoundError(
1020 "No such file or directory: '%s'" % (self.data)) from e
1020 "No such file or directory: '%s'" % (self.data)) from e
1021 md = {}
1021 md = {}
1022 if self.metadata:
1022 if self.metadata:
1023 md.update(self.metadata)
1023 md.update(self.metadata)
1024 if self.width:
1024 if self.width:
1025 md['width'] = self.width
1025 md['width'] = self.width
1026 if self.height:
1026 if self.height:
1027 md['height'] = self.height
1027 md['height'] = self.height
1028 if self.unconfined:
1028 if self.unconfined:
1029 md['unconfined'] = self.unconfined
1029 md['unconfined'] = self.unconfined
1030 if self.alt:
1030 if self.alt:
1031 md["alt"] = self.alt
1031 md["alt"] = self.alt
1032 if md or always_both:
1032 if md or always_both:
1033 return b64_data, md
1033 return b64_data, md
1034 else:
1034 else:
1035 return b64_data
1035 return b64_data
1036
1036
1037 def _repr_png_(self):
1037 def _repr_png_(self):
1038 if self.embed and self.format == self._FMT_PNG:
1038 if self.embed and self.format == self._FMT_PNG:
1039 return self._data_and_metadata()
1039 return self._data_and_metadata()
1040
1040
1041 def _repr_jpeg_(self):
1041 def _repr_jpeg_(self):
1042 if self.embed and self.format == self._FMT_JPEG:
1042 if self.embed and self.format == self._FMT_JPEG:
1043 return self._data_and_metadata()
1043 return self._data_and_metadata()
1044
1044
1045 def _find_ext(self, s):
1045 def _find_ext(self, s):
1046 base, ext = splitext(s)
1046 base, ext = splitext(s)
1047
1047
1048 if not ext:
1048 if not ext:
1049 return base
1049 return base
1050
1050
1051 # `splitext` includes leading period, so we skip it
1051 # `splitext` includes leading period, so we skip it
1052 return ext[1:].lower()
1052 return ext[1:].lower()
1053
1053
1054
1054
1055 class Video(DisplayObject):
1055 class Video(DisplayObject):
1056
1056
1057 def __init__(self, data=None, url=None, filename=None, embed=False,
1057 def __init__(self, data=None, url=None, filename=None, embed=False,
1058 mimetype=None, width=None, height=None, html_attributes="controls"):
1058 mimetype=None, width=None, height=None, html_attributes="controls"):
1059 """Create a video object given raw data or an URL.
1059 """Create a video object given raw data or an URL.
1060
1060
1061 When this object is returned by an input cell or passed to the
1061 When this object is returned by an input cell or passed to the
1062 display function, it will result in the video being displayed
1062 display function, it will result in the video being displayed
1063 in the frontend.
1063 in the frontend.
1064
1064
1065 Parameters
1065 Parameters
1066 ----------
1066 ----------
1067 data : unicode, str or bytes
1067 data : unicode, str or bytes
1068 The raw video data or a URL or filename to load the data from.
1068 The raw video data or a URL or filename to load the data from.
1069 Raw data will require passing ``embed=True``.
1069 Raw data will require passing ``embed=True``.
1070 url : unicode
1070 url : unicode
1071 A URL for the video. If you specify ``url=``,
1071 A URL for the video. If you specify ``url=``,
1072 the image data will not be embedded.
1072 the image data will not be embedded.
1073 filename : unicode
1073 filename : unicode
1074 Path to a local file containing the video.
1074 Path to a local file containing the video.
1075 Will be interpreted as a local URL unless ``embed=True``.
1075 Will be interpreted as a local URL unless ``embed=True``.
1076 embed : bool
1076 embed : bool
1077 Should the video be embedded using a data URI (True) or be
1077 Should the video be embedded using a data URI (True) or be
1078 loaded using a <video> tag (False).
1078 loaded using a <video> tag (False).
1079
1079
1080 Since videos are large, embedding them should be avoided, if possible.
1080 Since videos are large, embedding them should be avoided, if possible.
1081 You must confirm embedding as your intention by passing ``embed=True``.
1081 You must confirm embedding as your intention by passing ``embed=True``.
1082
1082
1083 Local files can be displayed with URLs without embedding the content, via::
1083 Local files can be displayed with URLs without embedding the content, via::
1084
1084
1085 Video('./video.mp4')
1085 Video('./video.mp4')
1086 mimetype : unicode
1086 mimetype : unicode
1087 Specify the mimetype for embedded videos.
1087 Specify the mimetype for embedded videos.
1088 Default will be guessed from file extension, if available.
1088 Default will be guessed from file extension, if available.
1089 width : int
1089 width : int
1090 Width in pixels to which to constrain the video in HTML.
1090 Width in pixels to which to constrain the video in HTML.
1091 If not supplied, defaults to the width of the video.
1091 If not supplied, defaults to the width of the video.
1092 height : int
1092 height : int
1093 Height in pixels to which to constrain the video in html.
1093 Height in pixels to which to constrain the video in html.
1094 If not supplied, defaults to the height of the video.
1094 If not supplied, defaults to the height of the video.
1095 html_attributes : str
1095 html_attributes : str
1096 Attributes for the HTML ``<video>`` block.
1096 Attributes for the HTML ``<video>`` block.
1097 Default: ``"controls"`` to get video controls.
1097 Default: ``"controls"`` to get video controls.
1098 Other examples: ``"controls muted"`` for muted video with controls,
1098 Other examples: ``"controls muted"`` for muted video with controls,
1099 ``"loop autoplay"`` for looping autoplaying video without controls.
1099 ``"loop autoplay"`` for looping autoplaying video without controls.
1100
1100
1101 Examples
1101 Examples
1102 --------
1102 --------
1103 ::
1103 ::
1104
1104
1105 Video('https://archive.org/download/Sita_Sings_the_Blues/Sita_Sings_the_Blues_small.mp4')
1105 Video('https://archive.org/download/Sita_Sings_the_Blues/Sita_Sings_the_Blues_small.mp4')
1106 Video('path/to/video.mp4')
1106 Video('path/to/video.mp4')
1107 Video('path/to/video.mp4', embed=True)
1107 Video('path/to/video.mp4', embed=True)
1108 Video('path/to/video.mp4', embed=True, html_attributes="controls muted autoplay")
1108 Video('path/to/video.mp4', embed=True, html_attributes="controls muted autoplay")
1109 Video(b'raw-videodata', embed=True)
1109 Video(b'raw-videodata', embed=True)
1110 """
1110 """
1111 if isinstance(data, (Path, PurePath)):
1111 if isinstance(data, (Path, PurePath)):
1112 data = str(data)
1112 data = str(data)
1113
1113
1114 if url is None and isinstance(data, str) and data.startswith(('http:', 'https:')):
1114 if url is None and isinstance(data, str) and data.startswith(('http:', 'https:')):
1115 url = data
1115 url = data
1116 data = None
1116 data = None
1117 elif data is not None and os.path.exists(data):
1117 elif data is not None and os.path.exists(data):
1118 filename = data
1118 filename = data
1119 data = None
1119 data = None
1120
1120
1121 if data and not embed:
1121 if data and not embed:
1122 msg = ''.join([
1122 msg = ''.join([
1123 "To embed videos, you must pass embed=True ",
1123 "To embed videos, you must pass embed=True ",
1124 "(this may make your notebook files huge)\n",
1124 "(this may make your notebook files huge)\n",
1125 "Consider passing Video(url='...')",
1125 "Consider passing Video(url='...')",
1126 ])
1126 ])
1127 raise ValueError(msg)
1127 raise ValueError(msg)
1128
1128
1129 self.mimetype = mimetype
1129 self.mimetype = mimetype
1130 self.embed = embed
1130 self.embed = embed
1131 self.width = width
1131 self.width = width
1132 self.height = height
1132 self.height = height
1133 self.html_attributes = html_attributes
1133 self.html_attributes = html_attributes
1134 super(Video, self).__init__(data=data, url=url, filename=filename)
1134 super(Video, self).__init__(data=data, url=url, filename=filename)
1135
1135
1136 def _repr_html_(self):
1136 def _repr_html_(self):
1137 width = height = ''
1137 width = height = ''
1138 if self.width:
1138 if self.width:
1139 width = ' width="%d"' % self.width
1139 width = ' width="%d"' % self.width
1140 if self.height:
1140 if self.height:
1141 height = ' height="%d"' % self.height
1141 height = ' height="%d"' % self.height
1142
1142
1143 # External URLs and potentially local files are not embedded into the
1143 # External URLs and potentially local files are not embedded into the
1144 # notebook output.
1144 # notebook output.
1145 if not self.embed:
1145 if not self.embed:
1146 url = self.url if self.url is not None else self.filename
1146 url = self.url if self.url is not None else self.filename
1147 output = """<video src="{0}" {1} {2} {3}>
1147 output = """<video src="{0}" {1} {2} {3}>
1148 Your browser does not support the <code>video</code> element.
1148 Your browser does not support the <code>video</code> element.
1149 </video>""".format(url, self.html_attributes, width, height)
1149 </video>""".format(url, self.html_attributes, width, height)
1150 return output
1150 return output
1151
1151
1152 # Embedded videos are base64-encoded.
1152 # Embedded videos are base64-encoded.
1153 mimetype = self.mimetype
1153 mimetype = self.mimetype
1154 if self.filename is not None:
1154 if self.filename is not None:
1155 if not mimetype:
1155 if not mimetype:
1156 mimetype, _ = mimetypes.guess_type(self.filename)
1156 mimetype, _ = mimetypes.guess_type(self.filename)
1157
1157
1158 with open(self.filename, 'rb') as f:
1158 with open(self.filename, 'rb') as f:
1159 video = f.read()
1159 video = f.read()
1160 else:
1160 else:
1161 video = self.data
1161 video = self.data
1162 if isinstance(video, str):
1162 if isinstance(video, str):
1163 # unicode input is already b64-encoded
1163 # unicode input is already b64-encoded
1164 b64_video = video
1164 b64_video = video
1165 else:
1165 else:
1166 b64_video = b2a_base64(video).decode('ascii').rstrip()
1166 b64_video = b2a_base64(video).decode('ascii').rstrip()
1167
1167
1168 output = """<video {0} {1} {2}>
1168 output = """<video {0} {1} {2}>
1169 <source src="data:{3};base64,{4}" type="{3}">
1169 <source src="data:{3};base64,{4}" type="{3}">
1170 Your browser does not support the video tag.
1170 Your browser does not support the video tag.
1171 </video>""".format(self.html_attributes, width, height, mimetype, b64_video)
1171 </video>""".format(self.html_attributes, width, height, mimetype, b64_video)
1172 return output
1172 return output
1173
1173
1174 def reload(self):
1174 def reload(self):
1175 # TODO
1175 # TODO
1176 pass
1176 pass
1177
1177
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
1185 In [1]: set_matplotlib_formats('png', 'jpeg', quality=90)
1190 In [1]: set_matplotlib_formats('png', 'jpeg', quality=90)
1186
1191
1187 To set this in your config files use the following::
1192 To set this in your config files use the following::
1188
1193
1189 c.InlineBackend.figure_formats = {'png', 'jpeg'}
1194 c.InlineBackend.figure_formats = {'png', 'jpeg'}
1190 c.InlineBackend.print_figure_kwargs.update({'quality' : 90})
1195 c.InlineBackend.print_figure_kwargs.update({'quality' : 90})
1191
1196
1192 Parameters
1197 Parameters
1193 ----------
1198 ----------
1194 *formats : strs
1199 *formats : strs
1195 One or more figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
1200 One or more figure formats to enable: 'png', 'retina', 'jpeg', 'svg', 'pdf'.
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
1216 plots in different cells won't interfere. Sometimes, you may want to make
1229 plots in different cells won't interfere. Sometimes, you may want to make
1217 a plot in one cell and then refine it in later cells. This can be accomplished
1230 a plot in one cell and then refine it in later cells. This can be accomplished
1218 by::
1231 by::
1219
1232
1220 In [1]: set_matplotlib_close(False)
1233 In [1]: set_matplotlib_close(False)
1221
1234
1222 To set this in your config files use the following::
1235 To set this in your config files use the following::
1223
1236
1224 c.InlineBackend.close_figures = False
1237 c.InlineBackend.close_figures = False
1225
1238
1226 Parameters
1239 Parameters
1227 ----------
1240 ----------
1228 close : bool
1241 close : bool
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)
@@ -1,367 +1,374 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Top-level display functions for displaying object in different formats."""
2 """Top-level display functions for displaying object in different formats."""
3
3
4 # Copyright (c) IPython Development Team.
4 # Copyright (c) IPython Development Team.
5 # Distributed under the terms of the Modified BSD License.
5 # Distributed under the terms of the Modified BSD License.
6
6
7
7
8 from binascii import b2a_hex
8 from binascii import b2a_hex
9 import os
9 import os
10 import sys
10 import sys
11
11
12 __all__ = ['display', 'clear_output', 'publish_display_data', 'update_display', 'DisplayHandle']
12 __all__ = ['display', 'clear_output', 'publish_display_data', 'update_display', 'DisplayHandle']
13
13
14 #-----------------------------------------------------------------------------
14 #-----------------------------------------------------------------------------
15 # utility functions
15 # utility functions
16 #-----------------------------------------------------------------------------
16 #-----------------------------------------------------------------------------
17
17
18
18
19 def _merge(d1, d2):
19 def _merge(d1, d2):
20 """Like update, but merges sub-dicts instead of clobbering at the top level.
20 """Like update, but merges sub-dicts instead of clobbering at the top level.
21
21
22 Updates d1 in-place
22 Updates d1 in-place
23 """
23 """
24
24
25 if not isinstance(d2, dict) or not isinstance(d1, dict):
25 if not isinstance(d2, dict) or not isinstance(d1, dict):
26 return d2
26 return d2
27 for key, value in d2.items():
27 for key, value in d2.items():
28 d1[key] = _merge(d1.get(key), value)
28 d1[key] = _merge(d1.get(key), value)
29 return d1
29 return d1
30
30
31
31
32 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
33 # Main functions
33 # Main functions
34 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
35
35
36
36
37 # use * to indicate transient is keyword-only
37 # use * to indicate transient is keyword-only
38 def publish_display_data(data, metadata=None, source=None, *, transient=None, **kwargs):
38 def publish_display_data(data, metadata=None, source=None, *, transient=None, **kwargs):
39 """Publish data and metadata to all frontends.
39 """Publish data and metadata to all frontends.
40
40
41 See the ``display_data`` message in the messaging documentation for
41 See the ``display_data`` message in the messaging documentation for
42 more details about this message type.
42 more details about this message type.
43
43
44 Keys of data and metadata can be any mime-type.
44 Keys of data and metadata can be any mime-type.
45
45
46 Parameters
46 Parameters
47 ----------
47 ----------
48 data : dict
48 data : dict
49 A dictionary having keys that are valid MIME types (like
49 A dictionary having keys that are valid MIME types (like
50 'text/plain' or 'image/svg+xml') and values that are the data for
50 'text/plain' or 'image/svg+xml') and values that are the data for
51 that MIME type. The data itself must be a JSON'able data
51 that MIME type. The data itself must be a JSON'able data
52 structure. Minimally all data should have the 'text/plain' data,
52 structure. Minimally all data should have the 'text/plain' data,
53 which can be displayed by all frontends. If more than the plain
53 which can be displayed by all frontends. If more than the plain
54 text is given, it is up to the frontend to decide which
54 text is given, it is up to the frontend to decide which
55 representation to use.
55 representation to use.
56 metadata : dict
56 metadata : dict
57 A dictionary for metadata related to the data. This can contain
57 A dictionary for metadata related to the data. This can contain
58 arbitrary key, value pairs that frontends can use to interpret
58 arbitrary key, value pairs that frontends can use to interpret
59 the data. mime-type keys matching those in data can be used
59 the data. mime-type keys matching those in data can be used
60 to specify metadata about particular representations.
60 to specify metadata about particular representations.
61 source : str, deprecated
61 source : str, deprecated
62 Unused.
62 Unused.
63 transient : dict, keyword-only
63 transient : dict, keyword-only
64 A dictionary of transient data, such as display_id.
64 A dictionary of transient data, such as display_id.
65 """
65 """
66 from IPython.core.interactiveshell import InteractiveShell
66 from IPython.core.interactiveshell import InteractiveShell
67
67
68 display_pub = InteractiveShell.instance().display_pub
68 display_pub = InteractiveShell.instance().display_pub
69
69
70 # only pass transient if supplied,
70 # only pass transient if supplied,
71 # to avoid errors with older ipykernel.
71 # to avoid errors with older ipykernel.
72 # TODO: We could check for ipykernel version and provide a detailed upgrade message.
72 # TODO: We could check for ipykernel version and provide a detailed upgrade message.
73 if transient:
73 if transient:
74 kwargs['transient'] = transient
74 kwargs['transient'] = transient
75
75
76 display_pub.publish(
76 display_pub.publish(
77 data=data,
77 data=data,
78 metadata=metadata,
78 metadata=metadata,
79 **kwargs
79 **kwargs
80 )
80 )
81
81
82
82
83 def _new_id():
83 def _new_id():
84 """Generate a new random text id with urandom"""
84 """Generate a new random text id with urandom"""
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.
92 Frontends can decide which representation is used and how.
102 Frontends can decide which representation is used and how.
93
103
94 In terminal IPython this will be similar to using :func:`print`, for use in richer
104 In terminal IPython this will be similar to using :func:`print`, for use in richer
95 frontends see Jupyter notebook examples with rich display logic.
105 frontends see Jupyter notebook examples with rich display logic.
96
106
97 Parameters
107 Parameters
98 ----------
108 ----------
99 *objs : object
109 *objs : object
100 The Python objects to display.
110 The Python objects to display.
101 raw : bool, optional
111 raw : bool, optional
102 Are the objects to be displayed already mimetype-keyed dicts of raw display data,
112 Are the objects to be displayed already mimetype-keyed dicts of raw display data,
103 or Python objects that need to be formatted before display? [default: False]
113 or Python objects that need to be formatted before display? [default: False]
104 include : list, tuple or set, optional
114 include : list, tuple or set, optional
105 A list of format type strings (MIME types) to include in the
115 A list of format type strings (MIME types) to include in the
106 format data dict. If this is set *only* the format types included
116 format data dict. If this is set *only* the format types included
107 in this list will be computed.
117 in this list will be computed.
108 exclude : list, tuple or set, optional
118 exclude : list, tuple or set, optional
109 A list of format type strings (MIME types) to exclude in the format
119 A list of format type strings (MIME types) to exclude in the format
110 data dict. If this is set all format types will be computed,
120 data dict. If this is set all format types will be computed,
111 except for those included in this argument.
121 except for those included in this argument.
112 metadata : dict, optional
122 metadata : dict, optional
113 A dictionary of metadata to associate with the output.
123 A dictionary of metadata to associate with the output.
114 mime-type keys in this dictionary will be associated with the individual
124 mime-type keys in this dictionary will be associated with the individual
115 representation formats, if they exist.
125 representation formats, if they exist.
116 transient : dict, optional
126 transient : dict, optional
117 A dictionary of transient data to associate with the output.
127 A dictionary of transient data to associate with the output.
118 Data in this dict should not be persisted to files (e.g. notebooks).
128 Data in this dict should not be persisted to files (e.g. notebooks).
119 display_id : str, bool optional
129 display_id : str, bool optional
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
132 (default).
144 (default).
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
140 ... def _repr_pretty_(self, pp, cycle):
151 ... def _repr_pretty_(self, pp, cycle):
141 ... import json
152 ... import json
142 ... pp.text(json.dumps(self.json, indent=2))
153 ... pp.text(json.dumps(self.json, indent=2))
143 ... def __repr__(self):
154 ... def __repr__(self):
144 ... return str(self.json)
155 ... return str(self.json)
145 ...
156 ...
146
157
147 >>> d = Json({1:2, 3: {4:5}})
158 >>> d = Json({1:2, 3: {4:5}})
148
159
149 >>> print(d)
160 >>> print(d)
150 {1: 2, 3: {4: 5}}
161 {1: 2, 3: {4: 5}}
151
162
152 >>> display(d)
163 >>> display(d)
153 {
164 {
154 "1": 2,
165 "1": 2,
155 "3": {
166 "3": {
156 "4": 5
167 "4": 5
157 }
168 }
158 }
169 }
159
170
160 >>> def int_formatter(integer, pp, cycle):
171 >>> def int_formatter(integer, pp, cycle):
161 ... pp.text('I'*integer)
172 ... pp.text('I'*integer)
162
173
163 >>> plain = get_ipython().display_formatter.formatters['text/plain']
174 >>> plain = get_ipython().display_formatter.formatters['text/plain']
164 >>> plain.for_type(int, int_formatter)
175 >>> plain.for_type(int, int_formatter)
165 <function _repr_pprint at 0x...>
176 <function _repr_pprint at 0x...>
166 >>> display(7-5)
177 >>> display(7-5)
167 II
178 II
168
179
169 >>> del plain.type_printers[int]
180 >>> del plain.type_printers[int]
170 >>> display(7-5)
181 >>> display(7-5)
171 2
182 2
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:
184
193
185 - HTML
194 - HTML
186 - JSON
195 - JSON
187 - PNG
196 - PNG
188 - JPEG
197 - JPEG
189 - SVG
198 - SVG
190 - LaTeX
199 - LaTeX
191
200
192 A single object can declare some or all of these representations; all are
201 A single object can declare some or all of these representations; all are
193 handled by IPython's display system.
202 handled by IPython's display system.
194
203
195 The main idea of the first approach is that you have to implement special
204 The main idea of the first approach is that you have to implement special
196 display methods when you define your class, one for each representation you
205 display methods when you define your class, one for each representation you
197 want to use. Here is a list of the names of the special methods and the
206 want to use. Here is a list of the names of the special methods and the
198 values they must return:
207 values they must return:
199
208
200 - `_repr_html_`: return raw HTML as a string, or a tuple (see below).
209 - `_repr_html_`: return raw HTML as a string, or a tuple (see below).
201 - `_repr_json_`: return a JSONable dict, or a tuple (see below).
210 - `_repr_json_`: return a JSONable dict, or a tuple (see below).
202 - `_repr_jpeg_`: return raw JPEG data, or a tuple (see below).
211 - `_repr_jpeg_`: return raw JPEG data, or a tuple (see below).
203 - `_repr_png_`: return raw PNG data, or a tuple (see below).
212 - `_repr_png_`: return raw PNG data, or a tuple (see below).
204 - `_repr_svg_`: return raw SVG data as a string, or a tuple (see below).
213 - `_repr_svg_`: return raw SVG data as a string, or a tuple (see below).
205 - `_repr_latex_`: return LaTeX commands in a string surrounded by "$",
214 - `_repr_latex_`: return LaTeX commands in a string surrounded by "$",
206 or a tuple (see below).
215 or a tuple (see below).
207 - `_repr_mimebundle_`: return a full mimebundle containing the mapping
216 - `_repr_mimebundle_`: return a full mimebundle containing the mapping
208 from all mimetypes to data.
217 from all mimetypes to data.
209 Use this for any mime-type not listed above.
218 Use this for any mime-type not listed above.
210
219
211 The above functions may also return the object's metadata alonside the
220 The above functions may also return the object's metadata alonside the
212 data. If the metadata is available, the functions will return a tuple
221 data. If the metadata is available, the functions will return a tuple
213 containing the data and metadata, in that order. If there is no metadata
222 containing the data and metadata, in that order. If there is no metadata
214 available, then the functions will return the data only.
223 available, then the functions will return the data only.
215
224
216 When you are directly writing your own classes, you can adapt them for
225 When you are directly writing your own classes, you can adapt them for
217 display in IPython by following the above approach. But in practice, you
226 display in IPython by following the above approach. But in practice, you
218 often need to work with existing classes that you can't easily modify.
227 often need to work with existing classes that you can't easily modify.
219
228
220 You can refer to the documentation on integrating with the display system in
229 You can refer to the documentation on integrating with the display system in
221 order to register custom formatters for already existing types
230 order to register custom formatters for already existing types
222 (:ref:`integrating_rich_display`).
231 (:ref:`integrating_rich_display`).
223
232
224 .. versionadded:: 5.4 display available without import
233 .. versionadded:: 5.4 display available without import
225 .. versionadded:: 6.1 display available without import
234 .. versionadded:: 6.1 display available without import
226
235
227 Since IPython 5.4 and 6.1 :func:`display` is automatically made available to
236 Since IPython 5.4 and 6.1 :func:`display` is automatically made available to
228 the user without import. If you are using display in a document that might
237 the user without import. If you are using display in a document that might
229 be used in a pure python context or with older version of IPython, use the
238 be used in a pure python context or with older version of IPython, use the
230 following import at the top of your file::
239 following import at the top of your file::
231
240
232 from IPython.display import display
241 from IPython.display import display
233
242
234 """
243 """
235 from IPython.core.interactiveshell import InteractiveShell
244 from IPython.core.interactiveshell import InteractiveShell
236
245
237 if not InteractiveShell.initialized():
246 if not InteractiveShell.initialized():
238 # Directly print objects.
247 # Directly print objects.
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:
246 metadata={}
254 metadata={}
247 if display_id:
255 if display_id:
248 if display_id is True:
256 if display_id is True:
249 display_id = _new_id()
257 display_id = _new_id()
250 transient['display_id'] = display_id
258 transient['display_id'] = display_id
251 if kwargs.get('update') and 'display_id' not in transient:
259 if kwargs.get('update') and 'display_id' not in transient:
252 raise TypeError('display_id required for update_display')
260 raise TypeError('display_id required for update_display')
253 if transient:
261 if transient:
254 kwargs['transient'] = transient
262 kwargs['transient'] = transient
255
263
256 if not objs and display_id:
264 if not objs and display_id:
257 # if given no objects, but still a request for a display_id,
265 # if given no objects, but still a request for a display_id,
258 # we assume the user wants to insert an empty output that
266 # we assume the user wants to insert an empty output that
259 # can be updated later
267 # can be updated later
260 objs = [{}]
268 objs = [{}]
261 raw = True
269 raw = True
262
270
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)
269 else:
280 else:
270 format_dict, md_dict = format(obj, include=include, exclude=exclude)
281 format_dict, md_dict = format(obj, include=include, exclude=exclude)
271 if not format_dict:
282 if not format_dict:
272 # nothing to display (e.g. _ipython_display_ took over)
283 # nothing to display (e.g. _ipython_display_ took over)
273 continue
284 continue
274 if metadata:
285 if metadata:
275 # kwarg-specified metadata gets precedence
286 # kwarg-specified metadata gets precedence
276 _merge(md_dict, metadata)
287 _merge(md_dict, metadata)
277 publish_display_data(data=format_dict, metadata=md_dict, **kwargs)
288 publish_display_data(data=format_dict, metadata=md_dict, **kwargs)
278 if display_id:
289 if display_id:
279 return DisplayHandle(display_id)
290 return DisplayHandle(display_id)
280
291
281
292
282 # use * for keyword-only display_id arg
293 # use * for keyword-only display_id arg
283 def update_display(obj, *, display_id, **kwargs):
294 def update_display(obj, *, display_id, **kwargs):
284 """Update an existing display by id
295 """Update an existing display by id
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
300 display(obj, display_id=display_id, **kwargs)
309 display(obj, display_id=display_id, **kwargs)
301
310
302
311
303 class DisplayHandle(object):
312 class DisplayHandle(object):
304 """A handle on an updatable display
313 """A handle on an updatable display
305
314
306 Call `.update(obj)` to display a new object.
315 Call `.update(obj)` to display a new object.
307
316
308 Call `.display(obj`) to add a new instance of this display,
317 Call `.display(obj`) to add a new instance of this display,
309 and update existing instances.
318 and update existing instances.
310
319
311 See Also
320 See Also
312 --------
321 --------
313
322
314 :func:`display`, :func:`update_display`
323 :func:`display`, :func:`update_display`
315
324
316 """
325 """
317
326
318 def __init__(self, display_id=None):
327 def __init__(self, display_id=None):
319 if display_id is None:
328 if display_id is None:
320 display_id = _new_id()
329 display_id = _new_id()
321 self.display_id = display_id
330 self.display_id = display_id
322
331
323 def __repr__(self):
332 def __repr__(self):
324 return "<%s display_id=%s>" % (self.__class__.__name__, self.display_id)
333 return "<%s display_id=%s>" % (self.__class__.__name__, self.display_id)
325
334
326 def display(self, obj, **kwargs):
335 def display(self, obj, **kwargs):
327 """Make a new display with my id, updating existing instances.
336 """Make a new display with my id, updating existing instances.
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)
338
346
339 def update(self, obj, **kwargs):
347 def update(self, obj, **kwargs):
340 """Update existing displays with my id
348 """Update existing displays with my id
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)
351
358
352
359
353 def clear_output(wait=False):
360 def clear_output(wait=False):
354 """Clear the output of the current cell receiving output.
361 """Clear the output of the current cell receiving output.
355
362
356 Parameters
363 Parameters
357 ----------
364 ----------
358 wait : bool [default: false]
365 wait : bool [default: false]
359 Wait to clear the output until new output is available to replace it."""
366 Wait to clear the output until new output is available to replace it."""
360 from IPython.core.interactiveshell import InteractiveShell
367 from IPython.core.interactiveshell import InteractiveShell
361 if InteractiveShell.initialized():
368 if InteractiveShell.initialized():
362 InteractiveShell.instance().display_pub.clear_output(wait)
369 InteractiveShell.instance().display_pub.clear_output(wait)
363 else:
370 else:
364 print('\033[2K\r', end='')
371 print('\033[2K\r', end='')
365 sys.stdout.flush()
372 sys.stdout.flush()
366 print('\033[2K\r', end='')
373 print('\033[2K\r', end='')
367 sys.stderr.flush()
374 sys.stderr.flush()
@@ -1,325 +1,325 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Displayhook for IPython.
2 """Displayhook for IPython.
3
3
4 This defines a callable class that IPython uses for `sys.displayhook`.
4 This defines a callable class that IPython uses for `sys.displayhook`.
5 """
5 """
6
6
7 # Copyright (c) IPython Development Team.
7 # Copyright (c) IPython Development Team.
8 # Distributed under the terms of the Modified BSD License.
8 # Distributed under the terms of the Modified BSD License.
9
9
10 import builtins as builtin_mod
10 import builtins as builtin_mod
11 import sys
11 import sys
12 import io as _io
12 import io as _io
13 import tokenize
13 import tokenize
14
14
15 from traitlets.config.configurable import Configurable
15 from traitlets.config.configurable import Configurable
16 from traitlets import Instance, Float
16 from traitlets import Instance, Float
17 from warnings import warn
17 from warnings import warn
18
18
19 # TODO: Move the various attributes (cache_size, [others now moved]). Some
19 # TODO: Move the various attributes (cache_size, [others now moved]). Some
20 # of these are also attributes of InteractiveShell. They should be on ONE object
20 # of these are also attributes of InteractiveShell. They should be on ONE object
21 # only and the other objects should ask that one object for their values.
21 # only and the other objects should ask that one object for their values.
22
22
23 class DisplayHook(Configurable):
23 class DisplayHook(Configurable):
24 """The custom IPython displayhook to replace sys.displayhook.
24 """The custom IPython displayhook to replace sys.displayhook.
25
25
26 This class does many things, but the basic idea is that it is a callable
26 This class does many things, but the basic idea is that it is a callable
27 that gets called anytime user code returns a value.
27 that gets called anytime user code returns a value.
28 """
28 """
29
29
30 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC',
30 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC',
31 allow_none=True)
31 allow_none=True)
32 exec_result = Instance('IPython.core.interactiveshell.ExecutionResult',
32 exec_result = Instance('IPython.core.interactiveshell.ExecutionResult',
33 allow_none=True)
33 allow_none=True)
34 cull_fraction = Float(0.2)
34 cull_fraction = Float(0.2)
35
35
36 def __init__(self, shell=None, cache_size=1000, **kwargs):
36 def __init__(self, shell=None, cache_size=1000, **kwargs):
37 super(DisplayHook, self).__init__(shell=shell, **kwargs)
37 super(DisplayHook, self).__init__(shell=shell, **kwargs)
38 cache_size_min = 3
38 cache_size_min = 3
39 if cache_size <= 0:
39 if cache_size <= 0:
40 self.do_full_cache = 0
40 self.do_full_cache = 0
41 cache_size = 0
41 cache_size = 0
42 elif cache_size < cache_size_min:
42 elif cache_size < cache_size_min:
43 self.do_full_cache = 0
43 self.do_full_cache = 0
44 cache_size = 0
44 cache_size = 0
45 warn('caching was disabled (min value for cache size is %s).' %
45 warn('caching was disabled (min value for cache size is %s).' %
46 cache_size_min,stacklevel=3)
46 cache_size_min,stacklevel=3)
47 else:
47 else:
48 self.do_full_cache = 1
48 self.do_full_cache = 1
49
49
50 self.cache_size = cache_size
50 self.cache_size = cache_size
51
51
52 # we need a reference to the user-level namespace
52 # we need a reference to the user-level namespace
53 self.shell = shell
53 self.shell = shell
54
54
55 self._,self.__,self.___ = '','',''
55 self._,self.__,self.___ = '','',''
56
56
57 # these are deliberately global:
57 # these are deliberately global:
58 to_user_ns = {'_':self._,'__':self.__,'___':self.___}
58 to_user_ns = {'_':self._,'__':self.__,'___':self.___}
59 self.shell.user_ns.update(to_user_ns)
59 self.shell.user_ns.update(to_user_ns)
60
60
61 @property
61 @property
62 def prompt_count(self):
62 def prompt_count(self):
63 return self.shell.execution_count
63 return self.shell.execution_count
64
64
65 #-------------------------------------------------------------------------
65 #-------------------------------------------------------------------------
66 # Methods used in __call__. Override these methods to modify the behavior
66 # Methods used in __call__. Override these methods to modify the behavior
67 # of the displayhook.
67 # of the displayhook.
68 #-------------------------------------------------------------------------
68 #-------------------------------------------------------------------------
69
69
70 def check_for_underscore(self):
70 def check_for_underscore(self):
71 """Check if the user has set the '_' variable by hand."""
71 """Check if the user has set the '_' variable by hand."""
72 # If something injected a '_' variable in __builtin__, delete
72 # If something injected a '_' variable in __builtin__, delete
73 # ipython's automatic one so we don't clobber that. gettext() in
73 # ipython's automatic one so we don't clobber that. gettext() in
74 # particular uses _, so we need to stay away from it.
74 # particular uses _, so we need to stay away from it.
75 if '_' in builtin_mod.__dict__:
75 if '_' in builtin_mod.__dict__:
76 try:
76 try:
77 user_value = self.shell.user_ns['_']
77 user_value = self.shell.user_ns['_']
78 if user_value is not self._:
78 if user_value is not self._:
79 return
79 return
80 del self.shell.user_ns['_']
80 del self.shell.user_ns['_']
81 except KeyError:
81 except KeyError:
82 pass
82 pass
83
83
84 def quiet(self):
84 def quiet(self):
85 """Should we silence the display hook because of ';'?"""
85 """Should we silence the display hook because of ';'?"""
86 # do not print output if input ends in ';'
86 # do not print output if input ends in ';'
87
87
88 try:
88 try:
89 cell = self.shell.history_manager.input_hist_parsed[-1]
89 cell = self.shell.history_manager.input_hist_parsed[-1]
90 except IndexError:
90 except IndexError:
91 # some uses of ipshellembed may fail here
91 # some uses of ipshellembed may fail here
92 return False
92 return False
93
93
94 sio = _io.StringIO(cell)
94 sio = _io.StringIO(cell)
95 tokens = list(tokenize.generate_tokens(sio.readline))
95 tokens = list(tokenize.generate_tokens(sio.readline))
96
96
97 for token in reversed(tokens):
97 for token in reversed(tokens):
98 if token[0] in (tokenize.ENDMARKER, tokenize.NL, tokenize.NEWLINE, tokenize.COMMENT):
98 if token[0] in (tokenize.ENDMARKER, tokenize.NL, tokenize.NEWLINE, tokenize.COMMENT):
99 continue
99 continue
100 if (token[0] == tokenize.OP) and (token[1] == ';'):
100 if (token[0] == tokenize.OP) and (token[1] == ';'):
101 return True
101 return True
102 else:
102 else:
103 return False
103 return False
104
104
105 def start_displayhook(self):
105 def start_displayhook(self):
106 """Start the displayhook, initializing resources."""
106 """Start the displayhook, initializing resources."""
107 pass
107 pass
108
108
109 def write_output_prompt(self):
109 def write_output_prompt(self):
110 """Write the output prompt.
110 """Write the output prompt.
111
111
112 The default implementation simply writes the prompt to
112 The default implementation simply writes the prompt to
113 ``sys.stdout``.
113 ``sys.stdout``.
114 """
114 """
115 # Use write, not print which adds an extra space.
115 # Use write, not print which adds an extra space.
116 sys.stdout.write(self.shell.separate_out)
116 sys.stdout.write(self.shell.separate_out)
117 outprompt = 'Out[{}]: '.format(self.shell.execution_count)
117 outprompt = 'Out[{}]: '.format(self.shell.execution_count)
118 if self.do_full_cache:
118 if self.do_full_cache:
119 sys.stdout.write(outprompt)
119 sys.stdout.write(outprompt)
120
120
121 def compute_format_data(self, result):
121 def compute_format_data(self, result):
122 """Compute format data of the object to be displayed.
122 """Compute format data of the object to be displayed.
123
123
124 The format data is a generalization of the :func:`repr` of an object.
124 The format data is a generalization of the :func:`repr` of an object.
125 In the default implementation the format data is a :class:`dict` of
125 In the default implementation the format data is a :class:`dict` of
126 key value pair where the keys are valid MIME types and the values
126 key value pair where the keys are valid MIME types and the values
127 are JSON'able data structure containing the raw data for that MIME
127 are JSON'able data structure containing the raw data for that MIME
128 type. It is up to frontends to determine pick a MIME to to use and
128 type. It is up to frontends to determine pick a MIME to to use and
129 display that data in an appropriate manner.
129 display that data in an appropriate manner.
130
130
131 This method only computes the format data for the object and should
131 This method only computes the format data for the object and should
132 NOT actually print or write that to a stream.
132 NOT actually print or write that to a stream.
133
133
134 Parameters
134 Parameters
135 ----------
135 ----------
136 result : object
136 result : object
137 The Python object passed to the display hook, whose format will be
137 The Python object passed to the display hook, whose format will be
138 computed.
138 computed.
139
139
140 Returns
140 Returns
141 -------
141 -------
142 (format_dict, md_dict) : dict
142 (format_dict, md_dict) : dict
143 format_dict is a :class:`dict` whose keys are valid MIME types and values are
143 format_dict is a :class:`dict` whose keys are valid MIME types and values are
144 JSON'able raw data for that MIME type. It is recommended that
144 JSON'able raw data for that MIME type. It is recommended that
145 all return values of this should always include the "text/plain"
145 all return values of this should always include the "text/plain"
146 MIME type representation of the object.
146 MIME type representation of the object.
147 md_dict is a :class:`dict` with the same MIME type keys
147 md_dict is a :class:`dict` with the same MIME type keys
148 of metadata associated with each output.
148 of metadata associated with each output.
149
149
150 """
150 """
151 return self.shell.display_formatter.format(result)
151 return self.shell.display_formatter.format(result)
152
152
153 # This can be set to True by the write_output_prompt method in a subclass
153 # This can be set to True by the write_output_prompt method in a subclass
154 prompt_end_newline = False
154 prompt_end_newline = False
155
155
156 def write_format_data(self, format_dict, md_dict=None) -> None:
156 def write_format_data(self, format_dict, md_dict=None) -> None:
157 """Write the format data dict to the frontend.
157 """Write the format data dict to the frontend.
158
158
159 This default version of this method simply writes the plain text
159 This default version of this method simply writes the plain text
160 representation of the object to ``sys.stdout``. Subclasses should
160 representation of the object to ``sys.stdout``. Subclasses should
161 override this method to send the entire `format_dict` to the
161 override this method to send the entire `format_dict` to the
162 frontends.
162 frontends.
163
163
164 Parameters
164 Parameters
165 ----------
165 ----------
166 format_dict : dict
166 format_dict : dict
167 The format dict for the object passed to `sys.displayhook`.
167 The format dict for the object passed to `sys.displayhook`.
168 md_dict : dict (optional)
168 md_dict : dict (optional)
169 The metadata dict to be associated with the display data.
169 The metadata dict to be associated with the display data.
170 """
170 """
171 if 'text/plain' not in format_dict:
171 if 'text/plain' not in format_dict:
172 # nothing to do
172 # nothing to do
173 return
173 return
174 # We want to print because we want to always make sure we have a
174 # We want to print because we want to always make sure we have a
175 # newline, even if all the prompt separators are ''. This is the
175 # newline, even if all the prompt separators are ''. This is the
176 # standard IPython behavior.
176 # standard IPython behavior.
177 result_repr = format_dict['text/plain']
177 result_repr = format_dict['text/plain']
178 if '\n' in result_repr:
178 if '\n' in result_repr:
179 # So that multi-line strings line up with the left column of
179 # So that multi-line strings line up with the left column of
180 # the screen, instead of having the output prompt mess up
180 # the screen, instead of having the output prompt mess up
181 # their first line.
181 # their first line.
182 # We use the prompt template instead of the expanded prompt
182 # We use the prompt template instead of the expanded prompt
183 # because the expansion may add ANSI escapes that will interfere
183 # because the expansion may add ANSI escapes that will interfere
184 # with our ability to determine whether or not we should add
184 # with our ability to determine whether or not we should add
185 # a newline.
185 # a newline.
186 if not self.prompt_end_newline:
186 if not self.prompt_end_newline:
187 # But avoid extraneous empty lines.
187 # But avoid extraneous empty lines.
188 result_repr = '\n' + result_repr
188 result_repr = '\n' + result_repr
189
189
190 try:
190 try:
191 print(result_repr)
191 print(result_repr)
192 except UnicodeEncodeError:
192 except UnicodeEncodeError:
193 # If a character is not supported by the terminal encoding replace
193 # If a character is not supported by the terminal encoding replace
194 # it with its \u or \x representation
194 # it with its \u or \x representation
195 print(result_repr.encode(sys.stdout.encoding,'backslashreplace').decode(sys.stdout.encoding))
195 print(result_repr.encode(sys.stdout.encoding,'backslashreplace').decode(sys.stdout.encoding))
196
196
197 def update_user_ns(self, result):
197 def update_user_ns(self, result):
198 """Update user_ns with various things like _, __, _1, etc."""
198 """Update user_ns with various things like _, __, _1, etc."""
199
199
200 # Avoid recursive reference when displaying _oh/Out
200 # Avoid recursive reference when displaying _oh/Out
201 if self.cache_size and result is not self.shell.user_ns['_oh']:
201 if self.cache_size and result is not self.shell.user_ns['_oh']:
202 if len(self.shell.user_ns['_oh']) >= self.cache_size and self.do_full_cache:
202 if len(self.shell.user_ns['_oh']) >= self.cache_size and self.do_full_cache:
203 self.cull_cache()
203 self.cull_cache()
204
204
205 # Don't overwrite '_' and friends if '_' is in __builtin__
205 # Don't overwrite '_' and friends if '_' is in __builtin__
206 # (otherwise we cause buggy behavior for things like gettext). and
206 # (otherwise we cause buggy behavior for things like gettext). and
207 # do not overwrite _, __ or ___ if one of these has been assigned
207 # do not overwrite _, __ or ___ if one of these has been assigned
208 # by the user.
208 # by the user.
209 update_unders = True
209 update_unders = True
210 for unders in ['_'*i for i in range(1,4)]:
210 for unders in ['_'*i for i in range(1,4)]:
211 if not unders in self.shell.user_ns:
211 if not unders in self.shell.user_ns:
212 continue
212 continue
213 if getattr(self, unders) is not self.shell.user_ns.get(unders):
213 if getattr(self, unders) is not self.shell.user_ns.get(unders):
214 update_unders = False
214 update_unders = False
215
215
216 self.___ = self.__
216 self.___ = self.__
217 self.__ = self._
217 self.__ = self._
218 self._ = result
218 self._ = result
219
219
220 if ('_' not in builtin_mod.__dict__) and (update_unders):
220 if ('_' not in builtin_mod.__dict__) and (update_unders):
221 self.shell.push({'_':self._,
221 self.shell.push({'_':self._,
222 '__':self.__,
222 '__':self.__,
223 '___':self.___}, interactive=False)
223 '___':self.___}, interactive=False)
224
224
225 # hackish access to top-level namespace to create _1,_2... dynamically
225 # hackish access to top-level namespace to create _1,_2... dynamically
226 to_main = {}
226 to_main = {}
227 if self.do_full_cache:
227 if self.do_full_cache:
228 new_result = '_%s' % self.prompt_count
228 new_result = '_%s' % self.prompt_count
229 to_main[new_result] = result
229 to_main[new_result] = result
230 self.shell.push(to_main, interactive=False)
230 self.shell.push(to_main, interactive=False)
231 self.shell.user_ns['_oh'][self.prompt_count] = result
231 self.shell.user_ns['_oh'][self.prompt_count] = result
232
232
233 def fill_exec_result(self, result):
233 def fill_exec_result(self, result):
234 if self.exec_result is not None:
234 if self.exec_result is not None:
235 self.exec_result.result = result
235 self.exec_result.result = result
236
236
237 def log_output(self, format_dict):
237 def log_output(self, format_dict):
238 """Log the output."""
238 """Log the output."""
239 if 'text/plain' not in format_dict:
239 if 'text/plain' not in format_dict:
240 # nothing to do
240 # nothing to do
241 return
241 return
242 if self.shell.logger.log_output:
242 if self.shell.logger.log_output:
243 self.shell.logger.log_write(format_dict['text/plain'], 'output')
243 self.shell.logger.log_write(format_dict['text/plain'], 'output')
244 self.shell.history_manager.output_hist_reprs[self.prompt_count] = \
244 self.shell.history_manager.output_hist_reprs[self.prompt_count] = \
245 format_dict['text/plain']
245 format_dict['text/plain']
246
246
247 def finish_displayhook(self):
247 def finish_displayhook(self):
248 """Finish up all displayhook activities."""
248 """Finish up all displayhook activities."""
249 sys.stdout.write(self.shell.separate_out2)
249 sys.stdout.write(self.shell.separate_out2)
250 sys.stdout.flush()
250 sys.stdout.flush()
251
251
252 def __call__(self, result=None):
252 def __call__(self, result=None):
253 """Printing with history cache management.
253 """Printing with history cache management.
254
254
255 This is invoked every time the interpreter needs to print, and is
255 This is invoked every time the interpreter needs to print, and is
256 activated by setting the variable sys.displayhook to it.
256 activated by setting the variable sys.displayhook to it.
257 """
257 """
258 self.check_for_underscore()
258 self.check_for_underscore()
259 if result is not None and not self.quiet():
259 if result is not None and not self.quiet():
260 self.start_displayhook()
260 self.start_displayhook()
261 self.write_output_prompt()
261 self.write_output_prompt()
262 format_dict, md_dict = self.compute_format_data(result)
262 format_dict, md_dict = self.compute_format_data(result)
263 self.update_user_ns(result)
263 self.update_user_ns(result)
264 self.fill_exec_result(result)
264 self.fill_exec_result(result)
265 if format_dict:
265 if format_dict:
266 self.write_format_data(format_dict, md_dict)
266 self.write_format_data(format_dict, md_dict)
267 self.log_output(format_dict)
267 self.log_output(format_dict)
268 self.finish_displayhook()
268 self.finish_displayhook()
269
269
270 def cull_cache(self):
270 def cull_cache(self):
271 """Output cache is full, cull the oldest entries"""
271 """Output cache is full, cull the oldest entries"""
272 oh = self.shell.user_ns.get('_oh', {})
272 oh = self.shell.user_ns.get('_oh', {})
273 sz = len(oh)
273 sz = len(oh)
274 cull_count = max(int(sz * self.cull_fraction), 2)
274 cull_count = max(int(sz * self.cull_fraction), 2)
275 warn('Output cache limit (currently {sz} entries) hit.\n'
275 warn('Output cache limit (currently {sz} entries) hit.\n'
276 'Flushing oldest {cull_count} entries.'.format(sz=sz, cull_count=cull_count))
276 'Flushing oldest {cull_count} entries.'.format(sz=sz, cull_count=cull_count))
277
277
278 for i, n in enumerate(sorted(oh)):
278 for i, n in enumerate(sorted(oh)):
279 if i >= cull_count:
279 if i >= cull_count:
280 break
280 break
281 self.shell.user_ns.pop('_%i' % n, None)
281 self.shell.user_ns.pop('_%i' % n, None)
282 oh.pop(n, None)
282 oh.pop(n, None)
283
283
284
284
285 def flush(self):
285 def flush(self):
286 if not self.do_full_cache:
286 if not self.do_full_cache:
287 raise ValueError("You shouldn't have reached the cache flush "
287 raise ValueError("You shouldn't have reached the cache flush "
288 "if full caching is not enabled!")
288 "if full caching is not enabled!")
289 # delete auto-generated vars from global namespace
289 # delete auto-generated vars from global namespace
290
290
291 for n in range(1,self.prompt_count + 1):
291 for n in range(1,self.prompt_count + 1):
292 key = '_'+repr(n)
292 key = '_'+repr(n)
293 try:
293 try:
294 del self.shell.user_ns[key]
294 del self.shell.user_ns[key]
295 except: pass
295 except: pass
296 # In some embedded circumstances, the user_ns doesn't have the
296 # In some embedded circumstances, the user_ns doesn't have the
297 # '_oh' key set up.
297 # '_oh' key set up.
298 oh = self.shell.user_ns.get('_oh', None)
298 oh = self.shell.user_ns.get('_oh', None)
299 if oh is not None:
299 if oh is not None:
300 oh.clear()
300 oh.clear()
301
301
302 # Release our own references to objects:
302 # Release our own references to objects:
303 self._, self.__, self.___ = '', '', ''
303 self._, self.__, self.___ = '', '', ''
304
304
305 if '_' not in builtin_mod.__dict__:
305 if '_' not in builtin_mod.__dict__:
306 self.shell.user_ns.update({'_':self._,'__':self.__,'___':self.___})
306 self.shell.user_ns.update({'_':self._,'__':self.__,'___':self.___})
307 import gc
307 import gc
308 # TODO: Is this really needed?
308 # TODO: Is this really needed?
309 # IronPython blocks here forever
309 # IronPython blocks here forever
310 if sys.platform != "cli":
310 if sys.platform != "cli":
311 gc.collect()
311 gc.collect()
312
312
313
313
314 class CapturingDisplayHook(object):
314 class CapturingDisplayHook(object):
315 def __init__(self, shell, outputs=None):
315 def __init__(self, shell, outputs=None):
316 self.shell = shell
316 self.shell = shell
317 if outputs is None:
317 if outputs is None:
318 outputs = []
318 outputs = []
319 self.outputs = outputs
319 self.outputs = outputs
320
320
321 def __call__(self, result=None):
321 def __call__(self, result=None):
322 if result is None:
322 if result is None:
323 return
323 return
324 format_dict, md_dict = self.shell.display_formatter.format(result)
324 format_dict, md_dict = self.shell.display_formatter.format(result)
325 self.outputs.append({ 'data': format_dict, 'metadata': md_dict })
325 self.outputs.append({ 'data': format_dict, 'metadata': md_dict })
@@ -1,138 +1,138 b''
1 """An interface for publishing rich data to frontends.
1 """An interface for publishing rich data to frontends.
2
2
3 There are two components of the display system:
3 There are two components of the display system:
4
4
5 * Display formatters, which take a Python object and compute the
5 * Display formatters, which take a Python object and compute the
6 representation of the object in various formats (text, HTML, SVG, etc.).
6 representation of the object in various formats (text, HTML, SVG, etc.).
7 * The display publisher that is used to send the representation data to the
7 * The display publisher that is used to send the representation data to the
8 various frontends.
8 various frontends.
9
9
10 This module defines the logic display publishing. The display publisher uses
10 This module defines the logic display publishing. The display publisher uses
11 the ``display_data`` message type that is defined in the IPython messaging
11 the ``display_data`` message type that is defined in the IPython messaging
12 spec.
12 spec.
13 """
13 """
14
14
15 # Copyright (c) IPython Development Team.
15 # Copyright (c) IPython Development Team.
16 # Distributed under the terms of the Modified BSD License.
16 # Distributed under the terms of the Modified BSD License.
17
17
18
18
19 import sys
19 import sys
20
20
21 from traitlets.config.configurable import Configurable
21 from traitlets.config.configurable import Configurable
22 from traitlets import List
22 from traitlets import List
23
23
24 # This used to be defined here - it is imported for backwards compatibility
24 # This used to be defined here - it is imported for backwards compatibility
25 from .display_functions import publish_display_data
25 from .display_functions import publish_display_data
26
26
27 #-----------------------------------------------------------------------------
27 #-----------------------------------------------------------------------------
28 # Main payload class
28 # Main payload class
29 #-----------------------------------------------------------------------------
29 #-----------------------------------------------------------------------------
30
30
31
31
32 class DisplayPublisher(Configurable):
32 class DisplayPublisher(Configurable):
33 """A traited class that publishes display data to frontends.
33 """A traited class that publishes display data to frontends.
34
34
35 Instances of this class are created by the main IPython object and should
35 Instances of this class are created by the main IPython object and should
36 be accessed there.
36 be accessed there.
37 """
37 """
38
38
39 def __init__(self, shell=None, *args, **kwargs):
39 def __init__(self, shell=None, *args, **kwargs):
40 self.shell = shell
40 self.shell = shell
41 super().__init__(*args, **kwargs)
41 super().__init__(*args, **kwargs)
42
42
43 def _validate_data(self, data, metadata=None):
43 def _validate_data(self, data, metadata=None):
44 """Validate the display data.
44 """Validate the display data.
45
45
46 Parameters
46 Parameters
47 ----------
47 ----------
48 data : dict
48 data : dict
49 The formata data dictionary.
49 The formata data dictionary.
50 metadata : dict
50 metadata : dict
51 Any metadata for the data.
51 Any metadata for the data.
52 """
52 """
53
53
54 if not isinstance(data, dict):
54 if not isinstance(data, dict):
55 raise TypeError('data must be a dict, got: %r' % data)
55 raise TypeError('data must be a dict, got: %r' % data)
56 if metadata is not None:
56 if metadata is not None:
57 if not isinstance(metadata, dict):
57 if not isinstance(metadata, dict):
58 raise TypeError('metadata must be a dict, got: %r' % data)
58 raise TypeError('metadata must be a dict, got: %r' % data)
59
59
60 # use * to indicate transient, update are keyword-only
60 # use * to indicate transient, update are keyword-only
61 def publish(self, data, metadata=None, source=None, *, transient=None, update=False, **kwargs) -> None:
61 def publish(self, data, metadata=None, source=None, *, transient=None, update=False, **kwargs) -> None:
62 """Publish data and metadata to all frontends.
62 """Publish data and metadata to all frontends.
63
63
64 See the ``display_data`` message in the messaging documentation for
64 See the ``display_data`` message in the messaging documentation for
65 more details about this message type.
65 more details about this message type.
66
66
67 The following MIME types are currently implemented:
67 The following MIME types are currently implemented:
68
68
69 * text/plain
69 * text/plain
70 * text/html
70 * text/html
71 * text/markdown
71 * text/markdown
72 * text/latex
72 * text/latex
73 * application/json
73 * application/json
74 * application/javascript
74 * application/javascript
75 * image/png
75 * image/png
76 * image/jpeg
76 * image/jpeg
77 * image/svg+xml
77 * image/svg+xml
78
78
79 Parameters
79 Parameters
80 ----------
80 ----------
81 data : dict
81 data : dict
82 A dictionary having keys that are valid MIME types (like
82 A dictionary having keys that are valid MIME types (like
83 'text/plain' or 'image/svg+xml') and values that are the data for
83 'text/plain' or 'image/svg+xml') and values that are the data for
84 that MIME type. The data itself must be a JSON'able data
84 that MIME type. The data itself must be a JSON'able data
85 structure. Minimally all data should have the 'text/plain' data,
85 structure. Minimally all data should have the 'text/plain' data,
86 which can be displayed by all frontends. If more than the plain
86 which can be displayed by all frontends. If more than the plain
87 text is given, it is up to the frontend to decide which
87 text is given, it is up to the frontend to decide which
88 representation to use.
88 representation to use.
89 metadata : dict
89 metadata : dict
90 A dictionary for metadata related to the data. This can contain
90 A dictionary for metadata related to the data. This can contain
91 arbitrary key, value pairs that frontends can use to interpret
91 arbitrary key, value pairs that frontends can use to interpret
92 the data. Metadata specific to each mime-type can be specified
92 the data. Metadata specific to each mime-type can be specified
93 in the metadata dict with the same mime-type keys as
93 in the metadata dict with the same mime-type keys as
94 the data itself.
94 the data itself.
95 source : str, deprecated
95 source : str, deprecated
96 Unused.
96 Unused.
97 transient: dict, keyword-only
97 transient : dict, keyword-only
98 A dictionary for transient data.
98 A dictionary for transient data.
99 Data in this dictionary should not be persisted as part of saving this output.
99 Data in this dictionary should not be persisted as part of saving this output.
100 Examples include 'display_id'.
100 Examples include 'display_id'.
101 update: bool, keyword-only, default: False
101 update : bool, keyword-only, default: False
102 If True, only update existing outputs with the same display_id,
102 If True, only update existing outputs with the same display_id,
103 rather than creating a new output.
103 rather than creating a new output.
104 """
104 """
105
105
106 handlers = {}
106 handlers = {}
107 if self.shell is not None:
107 if self.shell is not None:
108 handlers = getattr(self.shell, 'mime_renderers', {})
108 handlers = getattr(self.shell, 'mime_renderers', {})
109
109
110 for mime, handler in handlers.items():
110 for mime, handler in handlers.items():
111 if mime in data:
111 if mime in data:
112 handler(data[mime], metadata.get(mime, None))
112 handler(data[mime], metadata.get(mime, None))
113 return
113 return
114
114
115 if 'text/plain' in data:
115 if 'text/plain' in data:
116 print(data['text/plain'])
116 print(data['text/plain'])
117
117
118 def clear_output(self, wait=False):
118 def clear_output(self, wait=False):
119 """Clear the output of the cell receiving output."""
119 """Clear the output of the cell receiving output."""
120 print('\033[2K\r', end='')
120 print('\033[2K\r', end='')
121 sys.stdout.flush()
121 sys.stdout.flush()
122 print('\033[2K\r', end='')
122 print('\033[2K\r', end='')
123 sys.stderr.flush()
123 sys.stderr.flush()
124
124
125
125
126 class CapturingDisplayPublisher(DisplayPublisher):
126 class CapturingDisplayPublisher(DisplayPublisher):
127 """A DisplayPublisher that stores"""
127 """A DisplayPublisher that stores"""
128 outputs = List()
128 outputs = List()
129
129
130 def publish(self, data, metadata=None, source=None, *, transient=None, update=False):
130 def publish(self, data, metadata=None, source=None, *, transient=None, update=False):
131 self.outputs.append({'data':data, 'metadata':metadata,
131 self.outputs.append({'data':data, 'metadata':metadata,
132 'transient':transient, 'update':update})
132 'transient':transient, 'update':update})
133
133
134 def clear_output(self, wait=False):
134 def clear_output(self, wait=False):
135 super(CapturingDisplayPublisher, self).clear_output(wait)
135 super(CapturingDisplayPublisher, self).clear_output(wait)
136
136
137 # empty the list, *do not* reassign a new list
137 # empty the list, *do not* reassign a new list
138 self.outputs.clear()
138 self.outputs.clear()
@@ -1,161 +1,161 b''
1 """Infrastructure for registering and firing callbacks on application events.
1 """Infrastructure for registering and firing callbacks on application events.
2
2
3 Unlike :mod:`IPython.core.hooks`, which lets end users set single functions to
3 Unlike :mod:`IPython.core.hooks`, which lets end users set single functions to
4 be called at specific times, or a collection of alternative methods to try,
4 be called at specific times, or a collection of alternative methods to try,
5 callbacks are designed to be used by extension authors. A number of callbacks
5 callbacks are designed to be used by extension authors. A number of callbacks
6 can be registered for the same event without needing to be aware of one another.
6 can be registered for the same event without needing to be aware of one another.
7
7
8 The functions defined in this module are no-ops indicating the names of available
8 The functions defined in this module are no-ops indicating the names of available
9 events and the arguments which will be passed to them.
9 events and the arguments which will be passed to them.
10
10
11 .. note::
11 .. note::
12
12
13 This API is experimental in IPython 2.0, and may be revised in future versions.
13 This API is experimental in IPython 2.0, and may be revised in future versions.
14 """
14 """
15
15
16 from backcall import callback_prototype
16 from backcall import callback_prototype
17
17
18
18
19 class EventManager(object):
19 class EventManager(object):
20 """Manage a collection of events and a sequence of callbacks for each.
20 """Manage a collection of events and a sequence of callbacks for each.
21
21
22 This is attached to :class:`~IPython.core.interactiveshell.InteractiveShell`
22 This is attached to :class:`~IPython.core.interactiveshell.InteractiveShell`
23 instances as an ``events`` attribute.
23 instances as an ``events`` attribute.
24
24
25 .. note::
25 .. note::
26
26
27 This API is experimental in IPython 2.0, and may be revised in future versions.
27 This API is experimental in IPython 2.0, and may be revised in future versions.
28 """
28 """
29 def __init__(self, shell, available_events):
29 def __init__(self, shell, available_events):
30 """Initialise the :class:`CallbackManager`.
30 """Initialise the :class:`CallbackManager`.
31
31
32 Parameters
32 Parameters
33 ----------
33 ----------
34 shell
34 shell
35 The :class:`~IPython.core.interactiveshell.InteractiveShell` instance
35 The :class:`~IPython.core.interactiveshell.InteractiveShell` instance
36 available_callbacks
36 available_events
37 An iterable of names for callback events.
37 An iterable of names for callback events.
38 """
38 """
39 self.shell = shell
39 self.shell = shell
40 self.callbacks = {n:[] for n in available_events}
40 self.callbacks = {n:[] for n in available_events}
41
41
42 def register(self, event, function):
42 def register(self, event, function):
43 """Register a new event callback.
43 """Register a new event callback.
44
44
45 Parameters
45 Parameters
46 ----------
46 ----------
47 event : str
47 event : str
48 The event for which to register this callback.
48 The event for which to register this callback.
49 function : callable
49 function : callable
50 A function to be called on the given event. It should take the same
50 A function to be called on the given event. It should take the same
51 parameters as the appropriate callback prototype.
51 parameters as the appropriate callback prototype.
52
52
53 Raises
53 Raises
54 ------
54 ------
55 TypeError
55 TypeError
56 If ``function`` is not callable.
56 If ``function`` is not callable.
57 KeyError
57 KeyError
58 If ``event`` is not one of the known events.
58 If ``event`` is not one of the known events.
59 """
59 """
60 if not callable(function):
60 if not callable(function):
61 raise TypeError('Need a callable, got %r' % function)
61 raise TypeError('Need a callable, got %r' % function)
62 callback_proto = available_events.get(event)
62 callback_proto = available_events.get(event)
63 if function not in self.callbacks[event]:
63 if function not in self.callbacks[event]:
64 self.callbacks[event].append(callback_proto.adapt(function))
64 self.callbacks[event].append(callback_proto.adapt(function))
65
65
66 def unregister(self, event, function):
66 def unregister(self, event, function):
67 """Remove a callback from the given event."""
67 """Remove a callback from the given event."""
68 if function in self.callbacks[event]:
68 if function in self.callbacks[event]:
69 return self.callbacks[event].remove(function)
69 return self.callbacks[event].remove(function)
70
70
71 # Remove callback in case ``function`` was adapted by `backcall`.
71 # Remove callback in case ``function`` was adapted by `backcall`.
72 for callback in self.callbacks[event]:
72 for callback in self.callbacks[event]:
73 try:
73 try:
74 if callback.__wrapped__ is function:
74 if callback.__wrapped__ is function:
75 return self.callbacks[event].remove(callback)
75 return self.callbacks[event].remove(callback)
76 except AttributeError:
76 except AttributeError:
77 pass
77 pass
78
78
79 raise ValueError('Function {!r} is not registered as a {} callback'.format(function, event))
79 raise ValueError('Function {!r} is not registered as a {} callback'.format(function, event))
80
80
81 def trigger(self, event, *args, **kwargs):
81 def trigger(self, event, *args, **kwargs):
82 """Call callbacks for ``event``.
82 """Call callbacks for ``event``.
83
83
84 Any additional arguments are passed to all callbacks registered for this
84 Any additional arguments are passed to all callbacks registered for this
85 event. Exceptions raised by callbacks are caught, and a message printed.
85 event. Exceptions raised by callbacks are caught, and a message printed.
86 """
86 """
87 for func in self.callbacks[event][:]:
87 for func in self.callbacks[event][:]:
88 try:
88 try:
89 func(*args, **kwargs)
89 func(*args, **kwargs)
90 except (Exception, KeyboardInterrupt):
90 except (Exception, KeyboardInterrupt):
91 print("Error in callback {} (for {}):".format(func, event))
91 print("Error in callback {} (for {}):".format(func, event))
92 self.shell.showtraceback()
92 self.shell.showtraceback()
93
93
94 # event_name -> prototype mapping
94 # event_name -> prototype mapping
95 available_events = {}
95 available_events = {}
96
96
97 def _define_event(callback_function):
97 def _define_event(callback_function):
98 callback_proto = callback_prototype(callback_function)
98 callback_proto = callback_prototype(callback_function)
99 available_events[callback_function.__name__] = callback_proto
99 available_events[callback_function.__name__] = callback_proto
100 return callback_proto
100 return callback_proto
101
101
102 # ------------------------------------------------------------------------------
102 # ------------------------------------------------------------------------------
103 # Callback prototypes
103 # Callback prototypes
104 #
104 #
105 # No-op functions which describe the names of available events and the
105 # No-op functions which describe the names of available events and the
106 # signatures of callbacks for those events.
106 # signatures of callbacks for those events.
107 # ------------------------------------------------------------------------------
107 # ------------------------------------------------------------------------------
108
108
109 @_define_event
109 @_define_event
110 def pre_execute():
110 def pre_execute():
111 """Fires before code is executed in response to user/frontend action.
111 """Fires before code is executed in response to user/frontend action.
112
112
113 This includes comm and widget messages and silent execution, as well as user
113 This includes comm and widget messages and silent execution, as well as user
114 code cells.
114 code cells.
115 """
115 """
116 pass
116 pass
117
117
118 @_define_event
118 @_define_event
119 def pre_run_cell(info):
119 def pre_run_cell(info):
120 """Fires before user-entered code runs.
120 """Fires before user-entered code runs.
121
121
122 Parameters
122 Parameters
123 ----------
123 ----------
124 info : :class:`~IPython.core.interactiveshell.ExecutionInfo`
124 info : :class:`~IPython.core.interactiveshell.ExecutionInfo`
125 An object containing information used for the code execution.
125 An object containing information used for the code execution.
126 """
126 """
127 pass
127 pass
128
128
129 @_define_event
129 @_define_event
130 def post_execute():
130 def post_execute():
131 """Fires after code is executed in response to user/frontend action.
131 """Fires after code is executed in response to user/frontend action.
132
132
133 This includes comm and widget messages and silent execution, as well as user
133 This includes comm and widget messages and silent execution, as well as user
134 code cells.
134 code cells.
135 """
135 """
136 pass
136 pass
137
137
138 @_define_event
138 @_define_event
139 def post_run_cell(result):
139 def post_run_cell(result):
140 """Fires after user-entered code runs.
140 """Fires after user-entered code runs.
141
141
142 Parameters
142 Parameters
143 ----------
143 ----------
144 result : :class:`~IPython.core.interactiveshell.ExecutionResult`
144 result : :class:`~IPython.core.interactiveshell.ExecutionResult`
145 The object which will be returned as the execution result.
145 The object which will be returned as the execution result.
146 """
146 """
147 pass
147 pass
148
148
149 @_define_event
149 @_define_event
150 def shell_initialized(ip):
150 def shell_initialized(ip):
151 """Fires after initialisation of :class:`~IPython.core.interactiveshell.InteractiveShell`.
151 """Fires after initialisation of :class:`~IPython.core.interactiveshell.InteractiveShell`.
152
152
153 This is before extensions and startup scripts are loaded, so it can only be
153 This is before extensions and startup scripts are loaded, so it can only be
154 set by subclassing.
154 set by subclassing.
155
155
156 Parameters
156 Parameters
157 ----------
157 ----------
158 ip : :class:`~IPython.core.interactiveshell.InteractiveShell`
158 ip : :class:`~IPython.core.interactiveshell.InteractiveShell`
159 The newly initialised shell.
159 The newly initialised shell.
160 """
160 """
161 pass
161 pass
@@ -1,150 +1,150 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """A class for managing IPython extensions."""
2 """A class for managing IPython extensions."""
3
3
4 # Copyright (c) IPython Development Team.
4 # Copyright (c) IPython Development Team.
5 # Distributed under the terms of the Modified BSD License.
5 # Distributed under the terms of the Modified BSD License.
6
6
7 import os
7 import os
8 import os.path
8 import os.path
9 import sys
9 import sys
10 from importlib import import_module, reload
10 from importlib import import_module, reload
11
11
12 from traitlets.config.configurable import Configurable
12 from traitlets.config.configurable import Configurable
13 from IPython.utils.path import ensure_dir_exists, compress_user
13 from IPython.utils.path import ensure_dir_exists, compress_user
14 from IPython.utils.decorators import undoc
14 from IPython.utils.decorators import undoc
15 from traitlets import Instance
15 from traitlets import Instance
16
16
17
17
18 #-----------------------------------------------------------------------------
18 #-----------------------------------------------------------------------------
19 # Main class
19 # Main class
20 #-----------------------------------------------------------------------------
20 #-----------------------------------------------------------------------------
21
21
22 class ExtensionManager(Configurable):
22 class ExtensionManager(Configurable):
23 """A class to manage IPython extensions.
23 """A class to manage IPython extensions.
24
24
25 An IPython extension is an importable Python module that has
25 An IPython extension is an importable Python module that has
26 a function with the signature::
26 a function with the signature::
27
27
28 def load_ipython_extension(ipython):
28 def load_ipython_extension(ipython):
29 # Do things with ipython
29 # Do things with ipython
30
30
31 This function is called after your extension is imported and the
31 This function is called after your extension is imported and the
32 currently active :class:`InteractiveShell` instance is passed as
32 currently active :class:`InteractiveShell` instance is passed as
33 the only argument. You can do anything you want with IPython at
33 the only argument. You can do anything you want with IPython at
34 that point, including defining new magic and aliases, adding new
34 that point, including defining new magic and aliases, adding new
35 components, etc.
35 components, etc.
36
36
37 You can also optionally define an :func:`unload_ipython_extension(ipython)`
37 You can also optionally define an :func:`unload_ipython_extension(ipython)`
38 function, which will be called if the user unloads or reloads the extension.
38 function, which will be called if the user unloads or reloads the extension.
39 The extension manager will only call :func:`load_ipython_extension` again
39 The extension manager will only call :func:`load_ipython_extension` again
40 if the extension is reloaded.
40 if the extension is reloaded.
41
41
42 You can put your extension modules anywhere you want, as long as
42 You can put your extension modules anywhere you want, as long as
43 they can be imported by Python's standard import mechanism. However,
43 they can be imported by Python's standard import mechanism. However,
44 to make it easy to write extensions, you can also put your extensions
44 to make it easy to write extensions, you can also put your extensions
45 in ``os.path.join(self.ipython_dir, 'extensions')``. This directory
45 in ``os.path.join(self.ipython_dir, 'extensions')``. This directory
46 is added to ``sys.path`` automatically.
46 is added to ``sys.path`` automatically.
47 """
47 """
48
48
49 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC', allow_none=True)
49 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC', allow_none=True)
50
50
51 def __init__(self, shell=None, **kwargs):
51 def __init__(self, shell=None, **kwargs):
52 super(ExtensionManager, self).__init__(shell=shell, **kwargs)
52 super(ExtensionManager, self).__init__(shell=shell, **kwargs)
53 self.shell.observe(
53 self.shell.observe(
54 self._on_ipython_dir_changed, names=('ipython_dir',)
54 self._on_ipython_dir_changed, names=('ipython_dir',)
55 )
55 )
56 self.loaded = set()
56 self.loaded = set()
57
57
58 @property
58 @property
59 def ipython_extension_dir(self):
59 def ipython_extension_dir(self):
60 return os.path.join(self.shell.ipython_dir, u'extensions')
60 return os.path.join(self.shell.ipython_dir, u'extensions')
61
61
62 def _on_ipython_dir_changed(self, change):
62 def _on_ipython_dir_changed(self, change):
63 ensure_dir_exists(self.ipython_extension_dir)
63 ensure_dir_exists(self.ipython_extension_dir)
64
64
65 def load_extension(self, module_str):
65 def load_extension(self, module_str):
66 """Load an IPython extension by its module name.
66 """Load an IPython extension by its module name.
67
67
68 Returns the string "already loaded" if the extension is already loaded,
68 Returns the string "already loaded" if the extension is already loaded,
69 "no load function" if the module doesn't have a load_ipython_extension
69 "no load function" if the module doesn't have a load_ipython_extension
70 function, or None if it succeeded.
70 function, or None if it succeeded.
71 """
71 """
72 if module_str in self.loaded:
72 if module_str in self.loaded:
73 return "already loaded"
73 return "already loaded"
74
74
75 from IPython.utils.syspathcontext import prepended_to_syspath
75 from IPython.utils.syspathcontext import prepended_to_syspath
76
76
77 with self.shell.builtin_trap:
77 with self.shell.builtin_trap:
78 if module_str not in sys.modules:
78 if module_str not in sys.modules:
79 with prepended_to_syspath(self.ipython_extension_dir):
79 with prepended_to_syspath(self.ipython_extension_dir):
80 mod = import_module(module_str)
80 mod = import_module(module_str)
81 if mod.__file__.startswith(self.ipython_extension_dir):
81 if mod.__file__.startswith(self.ipython_extension_dir):
82 print(("Loading extensions from {dir} is deprecated. "
82 print(("Loading extensions from {dir} is deprecated. "
83 "We recommend managing extensions like any "
83 "We recommend managing extensions like any "
84 "other Python packages, in site-packages.").format(
84 "other Python packages, in site-packages.").format(
85 dir=compress_user(self.ipython_extension_dir)))
85 dir=compress_user(self.ipython_extension_dir)))
86 mod = sys.modules[module_str]
86 mod = sys.modules[module_str]
87 if self._call_load_ipython_extension(mod):
87 if self._call_load_ipython_extension(mod):
88 self.loaded.add(module_str)
88 self.loaded.add(module_str)
89 else:
89 else:
90 return "no load function"
90 return "no load function"
91
91
92 def unload_extension(self, module_str):
92 def unload_extension(self, module_str):
93 """Unload an IPython extension by its module name.
93 """Unload an IPython extension by its module name.
94
94
95 This function looks up the extension's name in ``sys.modules`` and
95 This function looks up the extension's name in ``sys.modules`` and
96 simply calls ``mod.unload_ipython_extension(self)``.
96 simply calls ``mod.unload_ipython_extension(self)``.
97
97
98 Returns the string "no unload function" if the extension doesn't define
98 Returns the string "no unload function" if the extension doesn't define
99 a function to unload itself, "not loaded" if the extension isn't loaded,
99 a function to unload itself, "not loaded" if the extension isn't loaded,
100 otherwise None.
100 otherwise None.
101 """
101 """
102 if module_str not in self.loaded:
102 if module_str not in self.loaded:
103 return "not loaded"
103 return "not loaded"
104
104
105 if module_str in sys.modules:
105 if module_str in sys.modules:
106 mod = sys.modules[module_str]
106 mod = sys.modules[module_str]
107 if self._call_unload_ipython_extension(mod):
107 if self._call_unload_ipython_extension(mod):
108 self.loaded.discard(module_str)
108 self.loaded.discard(module_str)
109 else:
109 else:
110 return "no unload function"
110 return "no unload function"
111
111
112 def reload_extension(self, module_str):
112 def reload_extension(self, module_str):
113 """Reload an IPython extension by calling reload.
113 """Reload an IPython extension by calling reload.
114
114
115 If the module has not been loaded before,
115 If the module has not been loaded before,
116 :meth:`InteractiveShell.load_extension` is called. Otherwise
116 :meth:`InteractiveShell.load_extension` is called. Otherwise
117 :func:`reload` is called and then the :func:`load_ipython_extension`
117 :func:`reload` is called and then the :func:`load_ipython_extension`
118 function of the module, if it exists is called.
118 function of the module, if it exists is called.
119 """
119 """
120 from IPython.utils.syspathcontext import prepended_to_syspath
120 from IPython.utils.syspathcontext import prepended_to_syspath
121
121
122 if (module_str in self.loaded) and (module_str in sys.modules):
122 if (module_str in self.loaded) and (module_str in sys.modules):
123 self.unload_extension(module_str)
123 self.unload_extension(module_str)
124 mod = sys.modules[module_str]
124 mod = sys.modules[module_str]
125 with prepended_to_syspath(self.ipython_extension_dir):
125 with prepended_to_syspath(self.ipython_extension_dir):
126 reload(mod)
126 reload(mod)
127 if self._call_load_ipython_extension(mod):
127 if self._call_load_ipython_extension(mod):
128 self.loaded.add(module_str)
128 self.loaded.add(module_str)
129 else:
129 else:
130 self.load_extension(module_str)
130 self.load_extension(module_str)
131
131
132 def _call_load_ipython_extension(self, mod):
132 def _call_load_ipython_extension(self, mod):
133 if hasattr(mod, 'load_ipython_extension'):
133 if hasattr(mod, 'load_ipython_extension'):
134 mod.load_ipython_extension(self.shell)
134 mod.load_ipython_extension(self.shell)
135 return True
135 return True
136
136
137 def _call_unload_ipython_extension(self, mod):
137 def _call_unload_ipython_extension(self, mod):
138 if hasattr(mod, 'unload_ipython_extension'):
138 if hasattr(mod, 'unload_ipython_extension'):
139 mod.unload_ipython_extension(self.shell)
139 mod.unload_ipython_extension(self.shell)
140 return True
140 return True
141
141
142 @undoc
142 @undoc
143 def install_extension(self, url, filename=None):
143 def install_extension(self, url, filename=None):
144 """
144 """
145 Deprecated.
145 Deprecated.
146 """
146 """
147 # Ensure the extension directory exists
147 # Ensure the extension directory exists
148 raise DeprecationWarning(
148 raise DeprecationWarning(
149 '`install_extension` and the `install_ext` magic have been deprecated since IPython 4.0'
149 '`install_extension` and the `install_ext` magic have been deprecated since IPython 4.0'
150 'Use pip or other package managers to manage ipython extensions.')
150 'Use pip or other package managers to manage ipython extensions.')
@@ -1,1024 +1,1026 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Display formatters.
2 """Display formatters.
3
3
4 Inheritance diagram:
4 Inheritance diagram:
5
5
6 .. inheritance-diagram:: IPython.core.formatters
6 .. inheritance-diagram:: IPython.core.formatters
7 :parts: 3
7 :parts: 3
8 """
8 """
9
9
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 import abc
13 import abc
14 import json
14 import json
15 import sys
15 import sys
16 import traceback
16 import traceback
17 import warnings
17 import warnings
18 from io import StringIO
18 from io import StringIO
19
19
20 from decorator import decorator
20 from decorator import decorator
21
21
22 from traitlets.config.configurable import Configurable
22 from traitlets.config.configurable import Configurable
23 from .getipython import get_ipython
23 from .getipython import get_ipython
24 from ..utils.sentinel import Sentinel
24 from ..utils.sentinel import Sentinel
25 from ..utils.dir2 import get_real_method
25 from ..utils.dir2 import get_real_method
26 from ..lib import pretty
26 from ..lib import pretty
27 from traitlets import (
27 from traitlets import (
28 Bool, Dict, Integer, Unicode, CUnicode, ObjectName, List,
28 Bool, Dict, Integer, Unicode, CUnicode, ObjectName, List,
29 ForwardDeclaredInstance,
29 ForwardDeclaredInstance,
30 default, observe,
30 default, observe,
31 )
31 )
32
32
33
33
34 class DisplayFormatter(Configurable):
34 class DisplayFormatter(Configurable):
35
35
36 active_types = List(Unicode(),
36 active_types = List(Unicode(),
37 help="""List of currently active mime-types to display.
37 help="""List of currently active mime-types to display.
38 You can use this to set a white-list for formats to display.
38 You can use this to set a white-list for formats to display.
39
39
40 Most users will not need to change this value.
40 Most users will not need to change this value.
41 """).tag(config=True)
41 """).tag(config=True)
42
42
43 @default('active_types')
43 @default('active_types')
44 def _active_types_default(self):
44 def _active_types_default(self):
45 return self.format_types
45 return self.format_types
46
46
47 @observe('active_types')
47 @observe('active_types')
48 def _active_types_changed(self, change):
48 def _active_types_changed(self, change):
49 for key, formatter in self.formatters.items():
49 for key, formatter in self.formatters.items():
50 if key in change['new']:
50 if key in change['new']:
51 formatter.enabled = True
51 formatter.enabled = True
52 else:
52 else:
53 formatter.enabled = False
53 formatter.enabled = False
54
54
55 ipython_display_formatter = ForwardDeclaredInstance('FormatterABC')
55 ipython_display_formatter = ForwardDeclaredInstance('FormatterABC')
56 @default('ipython_display_formatter')
56 @default('ipython_display_formatter')
57 def _default_formatter(self):
57 def _default_formatter(self):
58 return IPythonDisplayFormatter(parent=self)
58 return IPythonDisplayFormatter(parent=self)
59
59
60 mimebundle_formatter = ForwardDeclaredInstance('FormatterABC')
60 mimebundle_formatter = ForwardDeclaredInstance('FormatterABC')
61 @default('mimebundle_formatter')
61 @default('mimebundle_formatter')
62 def _default_mime_formatter(self):
62 def _default_mime_formatter(self):
63 return MimeBundleFormatter(parent=self)
63 return MimeBundleFormatter(parent=self)
64
64
65 # A dict of formatter whose keys are format types (MIME types) and whose
65 # A dict of formatter whose keys are format types (MIME types) and whose
66 # values are subclasses of BaseFormatter.
66 # values are subclasses of BaseFormatter.
67 formatters = Dict()
67 formatters = Dict()
68 @default('formatters')
68 @default('formatters')
69 def _formatters_default(self):
69 def _formatters_default(self):
70 """Activate the default formatters."""
70 """Activate the default formatters."""
71 formatter_classes = [
71 formatter_classes = [
72 PlainTextFormatter,
72 PlainTextFormatter,
73 HTMLFormatter,
73 HTMLFormatter,
74 MarkdownFormatter,
74 MarkdownFormatter,
75 SVGFormatter,
75 SVGFormatter,
76 PNGFormatter,
76 PNGFormatter,
77 PDFFormatter,
77 PDFFormatter,
78 JPEGFormatter,
78 JPEGFormatter,
79 LatexFormatter,
79 LatexFormatter,
80 JSONFormatter,
80 JSONFormatter,
81 JavascriptFormatter
81 JavascriptFormatter
82 ]
82 ]
83 d = {}
83 d = {}
84 for cls in formatter_classes:
84 for cls in formatter_classes:
85 f = cls(parent=self)
85 f = cls(parent=self)
86 d[f.format_type] = f
86 d[f.format_type] = f
87 return d
87 return d
88
88
89 def format(self, obj, include=None, exclude=None):
89 def format(self, obj, include=None, exclude=None):
90 """Return a format data dict for an object.
90 """Return a format data dict for an object.
91
91
92 By default all format types will be computed.
92 By default all format types will be computed.
93
93
94 The following MIME types are usually implemented:
94 The following MIME types are usually implemented:
95
95
96 * text/plain
96 * text/plain
97 * text/html
97 * text/html
98 * text/markdown
98 * text/markdown
99 * text/latex
99 * text/latex
100 * application/json
100 * application/json
101 * application/javascript
101 * application/javascript
102 * application/pdf
102 * application/pdf
103 * image/png
103 * image/png
104 * image/jpeg
104 * image/jpeg
105 * image/svg+xml
105 * image/svg+xml
106
106
107 Parameters
107 Parameters
108 ----------
108 ----------
109 obj : object
109 obj : object
110 The Python object whose format data will be computed.
110 The Python object whose format data will be computed.
111 include : list, tuple or set; optional
111 include : list, tuple or set; optional
112 A list of format type strings (MIME types) to include in the
112 A list of format type strings (MIME types) to include in the
113 format data dict. If this is set *only* the format types included
113 format data dict. If this is set *only* the format types included
114 in this list will be computed.
114 in this list will be computed.
115 exclude : list, tuple or set; optional
115 exclude : list, tuple or set; optional
116 A list of format type string (MIME types) to exclude in the format
116 A list of format type string (MIME types) to exclude in the format
117 data dict. If this is set all format types will be computed,
117 data dict. If this is set all format types will be computed,
118 except for those included in this argument.
118 except for those included in this argument.
119 Mimetypes present in exclude will take precedence over the ones in include
119 Mimetypes present in exclude will take precedence over the ones in include
120
120
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
128 data structure containing the raw data for the representation in
127 data structure containing the raw data for the representation in
129 that format.
128 that format.
130
129
131 metadata_dict is a dictionary of metadata about each mime-type output.
130 metadata_dict is a dictionary of metadata about each mime-type output.
132 Its keys will be a strict subset of the keys in format_dict.
131 Its keys will be a strict subset of the keys in format_dict.
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
140 not be called.
138 not be called.
141
139
142 """
140 """
143 format_dict = {}
141 format_dict = {}
144 md_dict = {}
142 md_dict = {}
145
143
146 if self.ipython_display_formatter(obj):
144 if self.ipython_display_formatter(obj):
147 # object handled itself, don't proceed
145 # object handled itself, don't proceed
148 return {}, {}
146 return {}, {}
149
147
150 format_dict, md_dict = self.mimebundle_formatter(obj, include=include, exclude=exclude)
148 format_dict, md_dict = self.mimebundle_formatter(obj, include=include, exclude=exclude)
151
149
152 if format_dict or md_dict:
150 if format_dict or md_dict:
153 if include:
151 if include:
154 format_dict = {k:v for k,v in format_dict.items() if k in include}
152 format_dict = {k:v for k,v in format_dict.items() if k in include}
155 md_dict = {k:v for k,v in md_dict.items() if k in include}
153 md_dict = {k:v for k,v in md_dict.items() if k in include}
156 if exclude:
154 if exclude:
157 format_dict = {k:v for k,v in format_dict.items() if k not in exclude}
155 format_dict = {k:v for k,v in format_dict.items() if k not in exclude}
158 md_dict = {k:v for k,v in md_dict.items() if k not in exclude}
156 md_dict = {k:v for k,v in md_dict.items() if k not in exclude}
159
157
160 for format_type, formatter in self.formatters.items():
158 for format_type, formatter in self.formatters.items():
161 if format_type in format_dict:
159 if format_type in format_dict:
162 # already got it from mimebundle, maybe don't render again.
160 # already got it from mimebundle, maybe don't render again.
163 # exception: manually registered per-mime renderer
161 # exception: manually registered per-mime renderer
164 # check priority:
162 # check priority:
165 # 1. user-registered per-mime formatter
163 # 1. user-registered per-mime formatter
166 # 2. mime-bundle (user-registered or repr method)
164 # 2. mime-bundle (user-registered or repr method)
167 # 3. default per-mime formatter (e.g. repr method)
165 # 3. default per-mime formatter (e.g. repr method)
168 try:
166 try:
169 formatter.lookup(obj)
167 formatter.lookup(obj)
170 except KeyError:
168 except KeyError:
171 # no special formatter, use mime-bundle-provided value
169 # no special formatter, use mime-bundle-provided value
172 continue
170 continue
173 if include and format_type not in include:
171 if include and format_type not in include:
174 continue
172 continue
175 if exclude and format_type in exclude:
173 if exclude and format_type in exclude:
176 continue
174 continue
177
175
178 md = None
176 md = None
179 try:
177 try:
180 data = formatter(obj)
178 data = formatter(obj)
181 except:
179 except:
182 # FIXME: log the exception
180 # FIXME: log the exception
183 raise
181 raise
184
182
185 # formatters can return raw data or (data, metadata)
183 # formatters can return raw data or (data, metadata)
186 if isinstance(data, tuple) and len(data) == 2:
184 if isinstance(data, tuple) and len(data) == 2:
187 data, md = data
185 data, md = data
188
186
189 if data is not None:
187 if data is not None:
190 format_dict[format_type] = data
188 format_dict[format_type] = data
191 if md is not None:
189 if md is not None:
192 md_dict[format_type] = md
190 md_dict[format_type] = md
193 return format_dict, md_dict
191 return format_dict, md_dict
194
192
195 @property
193 @property
196 def format_types(self):
194 def format_types(self):
197 """Return the format types (MIME types) of the active formatters."""
195 """Return the format types (MIME types) of the active formatters."""
198 return list(self.formatters.keys())
196 return list(self.formatters.keys())
199
197
200
198
201 #-----------------------------------------------------------------------------
199 #-----------------------------------------------------------------------------
202 # Formatters for specific format types (text, html, svg, etc.)
200 # Formatters for specific format types (text, html, svg, etc.)
203 #-----------------------------------------------------------------------------
201 #-----------------------------------------------------------------------------
204
202
205
203
206 def _safe_repr(obj):
204 def _safe_repr(obj):
207 """Try to return a repr of an object
205 """Try to return a repr of an object
208
206
209 always returns a string, at least.
207 always returns a string, at least.
210 """
208 """
211 try:
209 try:
212 return repr(obj)
210 return repr(obj)
213 except Exception as e:
211 except Exception as e:
214 return "un-repr-able object (%r)" % e
212 return "un-repr-able object (%r)" % e
215
213
216
214
217 class FormatterWarning(UserWarning):
215 class FormatterWarning(UserWarning):
218 """Warning class for errors in formatters"""
216 """Warning class for errors in formatters"""
219
217
220 @decorator
218 @decorator
221 def catch_format_error(method, self, *args, **kwargs):
219 def catch_format_error(method, self, *args, **kwargs):
222 """show traceback on failed format call"""
220 """show traceback on failed format call"""
223 try:
221 try:
224 r = method(self, *args, **kwargs)
222 r = method(self, *args, **kwargs)
225 except NotImplementedError:
223 except NotImplementedError:
226 # don't warn on NotImplementedErrors
224 # don't warn on NotImplementedErrors
227 return self._check_return(None, args[0])
225 return self._check_return(None, args[0])
228 except Exception:
226 except Exception:
229 exc_info = sys.exc_info()
227 exc_info = sys.exc_info()
230 ip = get_ipython()
228 ip = get_ipython()
231 if ip is not None:
229 if ip is not None:
232 ip.showtraceback(exc_info)
230 ip.showtraceback(exc_info)
233 else:
231 else:
234 traceback.print_exception(*exc_info)
232 traceback.print_exception(*exc_info)
235 return self._check_return(None, args[0])
233 return self._check_return(None, args[0])
236 return self._check_return(r, args[0])
234 return self._check_return(r, args[0])
237
235
238
236
239 class FormatterABC(metaclass=abc.ABCMeta):
237 class FormatterABC(metaclass=abc.ABCMeta):
240 """ Abstract base class for Formatters.
238 """ Abstract base class for Formatters.
241
239
242 A formatter is a callable class that is responsible for computing the
240 A formatter is a callable class that is responsible for computing the
243 raw format data for a particular format type (MIME type). For example,
241 raw format data for a particular format type (MIME type). For example,
244 an HTML formatter would have a format type of `text/html` and would return
242 an HTML formatter would have a format type of `text/html` and would return
245 the HTML representation of the object when called.
243 the HTML representation of the object when called.
246 """
244 """
247
245
248 # The format type of the data returned, usually a MIME type.
246 # The format type of the data returned, usually a MIME type.
249 format_type = 'text/plain'
247 format_type = 'text/plain'
250
248
251 # Is the formatter enabled...
249 # Is the formatter enabled...
252 enabled = True
250 enabled = True
253
251
254 @abc.abstractmethod
252 @abc.abstractmethod
255 def __call__(self, obj):
253 def __call__(self, obj):
256 """Return a JSON'able representation of the object.
254 """Return a JSON'able representation of the object.
257
255
258 If the object cannot be formatted by this formatter,
256 If the object cannot be formatted by this formatter,
259 warn and return None.
257 warn and return None.
260 """
258 """
261 return repr(obj)
259 return repr(obj)
262
260
263
261
264 def _mod_name_key(typ):
262 def _mod_name_key(typ):
265 """Return a (__module__, __name__) tuple for a type.
263 """Return a (__module__, __name__) tuple for a type.
266
264
267 Used as key in Formatter.deferred_printers.
265 Used as key in Formatter.deferred_printers.
268 """
266 """
269 module = getattr(typ, '__module__', None)
267 module = getattr(typ, '__module__', None)
270 name = getattr(typ, '__name__', None)
268 name = getattr(typ, '__name__', None)
271 return (module, name)
269 return (module, name)
272
270
273
271
274 def _get_type(obj):
272 def _get_type(obj):
275 """Return the type of an instance (old and new-style)"""
273 """Return the type of an instance (old and new-style)"""
276 return getattr(obj, '__class__', None) or type(obj)
274 return getattr(obj, '__class__', None) or type(obj)
277
275
278
276
279 _raise_key_error = Sentinel('_raise_key_error', __name__,
277 _raise_key_error = Sentinel('_raise_key_error', __name__,
280 """
278 """
281 Special value to raise a KeyError
279 Special value to raise a KeyError
282
280
283 Raise KeyError in `BaseFormatter.pop` if passed as the default value to `pop`
281 Raise KeyError in `BaseFormatter.pop` if passed as the default value to `pop`
284 """)
282 """)
285
283
286
284
287 class BaseFormatter(Configurable):
285 class BaseFormatter(Configurable):
288 """A base formatter class that is configurable.
286 """A base formatter class that is configurable.
289
287
290 This formatter should usually be used as the base class of all formatters.
288 This formatter should usually be used as the base class of all formatters.
291 It is a traited :class:`Configurable` class and includes an extensible
289 It is a traited :class:`Configurable` class and includes an extensible
292 API for users to determine how their objects are formatted. The following
290 API for users to determine how their objects are formatted. The following
293 logic is used to find a function to format an given object.
291 logic is used to find a function to format an given object.
294
292
295 1. The object is introspected to see if it has a method with the name
293 1. The object is introspected to see if it has a method with the name
296 :attr:`print_method`. If is does, that object is passed to that method
294 :attr:`print_method`. If is does, that object is passed to that method
297 for formatting.
295 for formatting.
298 2. If no print method is found, three internal dictionaries are consulted
296 2. If no print method is found, three internal dictionaries are consulted
299 to find print method: :attr:`singleton_printers`, :attr:`type_printers`
297 to find print method: :attr:`singleton_printers`, :attr:`type_printers`
300 and :attr:`deferred_printers`.
298 and :attr:`deferred_printers`.
301
299
302 Users should use these dictionaries to register functions that will be
300 Users should use these dictionaries to register functions that will be
303 used to compute the format data for their objects (if those objects don't
301 used to compute the format data for their objects (if those objects don't
304 have the special print methods). The easiest way of using these
302 have the special print methods). The easiest way of using these
305 dictionaries is through the :meth:`for_type` and :meth:`for_type_by_name`
303 dictionaries is through the :meth:`for_type` and :meth:`for_type_by_name`
306 methods.
304 methods.
307
305
308 If no function/callable is found to compute the format data, ``None`` is
306 If no function/callable is found to compute the format data, ``None`` is
309 returned and this format type is not used.
307 returned and this format type is not used.
310 """
308 """
311
309
312 format_type = Unicode('text/plain')
310 format_type = Unicode('text/plain')
313 _return_type = str
311 _return_type = str
314
312
315 enabled = Bool(True).tag(config=True)
313 enabled = Bool(True).tag(config=True)
316
314
317 print_method = ObjectName('__repr__')
315 print_method = ObjectName('__repr__')
318
316
319 # The singleton printers.
317 # The singleton printers.
320 # Maps the IDs of the builtin singleton objects to the format functions.
318 # Maps the IDs of the builtin singleton objects to the format functions.
321 singleton_printers = Dict().tag(config=True)
319 singleton_printers = Dict().tag(config=True)
322
320
323 # The type-specific printers.
321 # The type-specific printers.
324 # Map type objects to the format functions.
322 # Map type objects to the format functions.
325 type_printers = Dict().tag(config=True)
323 type_printers = Dict().tag(config=True)
326
324
327 # The deferred-import type-specific printers.
325 # The deferred-import type-specific printers.
328 # Map (modulename, classname) pairs to the format functions.
326 # Map (modulename, classname) pairs to the format functions.
329 deferred_printers = Dict().tag(config=True)
327 deferred_printers = Dict().tag(config=True)
330
328
331 @catch_format_error
329 @catch_format_error
332 def __call__(self, obj):
330 def __call__(self, obj):
333 """Compute the format for an object."""
331 """Compute the format for an object."""
334 if self.enabled:
332 if self.enabled:
335 # lookup registered printer
333 # lookup registered printer
336 try:
334 try:
337 printer = self.lookup(obj)
335 printer = self.lookup(obj)
338 except KeyError:
336 except KeyError:
339 pass
337 pass
340 else:
338 else:
341 return printer(obj)
339 return printer(obj)
342 # Finally look for special method names
340 # Finally look for special method names
343 method = get_real_method(obj, self.print_method)
341 method = get_real_method(obj, self.print_method)
344 if method is not None:
342 if method is not None:
345 return method()
343 return method()
346 return None
344 return None
347 else:
345 else:
348 return None
346 return None
349
347
350 def __contains__(self, typ):
348 def __contains__(self, typ):
351 """map in to lookup_by_type"""
349 """map in to lookup_by_type"""
352 try:
350 try:
353 self.lookup_by_type(typ)
351 self.lookup_by_type(typ)
354 except KeyError:
352 except KeyError:
355 return False
353 return False
356 else:
354 else:
357 return True
355 return True
358
356
359 def _check_return(self, r, obj):
357 def _check_return(self, r, obj):
360 """Check that a return value is appropriate
358 """Check that a return value is appropriate
361
359
362 Return the value if so, None otherwise, warning if invalid.
360 Return the value if so, None otherwise, warning if invalid.
363 """
361 """
364 if r is None or isinstance(r, self._return_type) or \
362 if r is None or isinstance(r, self._return_type) or \
365 (isinstance(r, tuple) and r and isinstance(r[0], self._return_type)):
363 (isinstance(r, tuple) and r and isinstance(r[0], self._return_type)):
366 return r
364 return r
367 else:
365 else:
368 warnings.warn(
366 warnings.warn(
369 "%s formatter returned invalid type %s (expected %s) for object: %s" % \
367 "%s formatter returned invalid type %s (expected %s) for object: %s" % \
370 (self.format_type, type(r), self._return_type, _safe_repr(obj)),
368 (self.format_type, type(r), self._return_type, _safe_repr(obj)),
371 FormatterWarning
369 FormatterWarning
372 )
370 )
373
371
374 def lookup(self, obj):
372 def lookup(self, obj):
375 """Look up the formatter for a given instance.
373 """Look up the formatter for a given instance.
376
374
377 Parameters
375 Parameters
378 ----------
376 ----------
379 obj : object instance
377 obj : object instance
380
378
381 Returns
379 Returns
382 -------
380 -------
383 f : callable
381 f : callable
384 The registered formatting callable for the type.
382 The registered formatting callable for the type.
385
383
386 Raises
384 Raises
387 ------
385 ------
388 KeyError if the type has not been registered.
386 KeyError if the type has not been registered.
389 """
387 """
390 # look for singleton first
388 # look for singleton first
391 obj_id = id(obj)
389 obj_id = id(obj)
392 if obj_id in self.singleton_printers:
390 if obj_id in self.singleton_printers:
393 return self.singleton_printers[obj_id]
391 return self.singleton_printers[obj_id]
394 # then lookup by type
392 # then lookup by type
395 return self.lookup_by_type(_get_type(obj))
393 return self.lookup_by_type(_get_type(obj))
396
394
397 def lookup_by_type(self, typ):
395 def lookup_by_type(self, typ):
398 """Look up the registered formatter for a type.
396 """Look up the registered formatter for a type.
399
397
400 Parameters
398 Parameters
401 ----------
399 ----------
402 typ : type or '__module__.__name__' string for a type
400 typ : type or '__module__.__name__' string for a type
403
401
404 Returns
402 Returns
405 -------
403 -------
406 f : callable
404 f : callable
407 The registered formatting callable for the type.
405 The registered formatting callable for the type.
408
406
409 Raises
407 Raises
410 ------
408 ------
411 KeyError if the type has not been registered.
409 KeyError if the type has not been registered.
412 """
410 """
413 if isinstance(typ, str):
411 if isinstance(typ, str):
414 typ_key = tuple(typ.rsplit('.',1))
412 typ_key = tuple(typ.rsplit('.',1))
415 if typ_key not in self.deferred_printers:
413 if typ_key not in self.deferred_printers:
416 # We may have it cached in the type map. We will have to
414 # We may have it cached in the type map. We will have to
417 # iterate over all of the types to check.
415 # iterate over all of the types to check.
418 for cls in self.type_printers:
416 for cls in self.type_printers:
419 if _mod_name_key(cls) == typ_key:
417 if _mod_name_key(cls) == typ_key:
420 return self.type_printers[cls]
418 return self.type_printers[cls]
421 else:
419 else:
422 return self.deferred_printers[typ_key]
420 return self.deferred_printers[typ_key]
423 else:
421 else:
424 for cls in pretty._get_mro(typ):
422 for cls in pretty._get_mro(typ):
425 if cls in self.type_printers or self._in_deferred_types(cls):
423 if cls in self.type_printers or self._in_deferred_types(cls):
426 return self.type_printers[cls]
424 return self.type_printers[cls]
427
425
428 # If we have reached here, the lookup failed.
426 # If we have reached here, the lookup failed.
429 raise KeyError("No registered printer for {0!r}".format(typ))
427 raise KeyError("No registered printer for {0!r}".format(typ))
430
428
431 def for_type(self, typ, func=None):
429 def for_type(self, typ, func=None):
432 """Add a format function for a given type.
430 """Add a format function for a given type.
433
431
434 Parameters
432 Parameters
435 ----------
433 ----------
436 typ : type or '__module__.__name__' string for a type
434 typ : type or '__module__.__name__' string for a type
437 The class of the object that will be formatted using `func`.
435 The class of the object that will be formatted using `func`.
438 func : callable
436 func : callable
439 A callable for computing the format data.
437 A callable for computing the format data.
440 `func` will be called with the object to be formatted,
438 `func` will be called with the object to be formatted,
441 and will return the raw data in this formatter's format.
439 and will return the raw data in this formatter's format.
442 Subclasses may use a different call signature for the
440 Subclasses may use a different call signature for the
443 `func` argument.
441 `func` argument.
444
442
445 If `func` is None or not specified, there will be no change,
443 If `func` is None or not specified, there will be no change,
446 only returning the current value.
444 only returning the current value.
447
445
448 Returns
446 Returns
449 -------
447 -------
450 oldfunc : callable
448 oldfunc : callable
451 The currently registered callable.
449 The currently registered callable.
452 If you are registering a new formatter,
450 If you are registering a new formatter,
453 this will be the previous value (to enable restoring later).
451 this will be the previous value (to enable restoring later).
454 """
452 """
455 # if string given, interpret as 'pkg.module.class_name'
453 # if string given, interpret as 'pkg.module.class_name'
456 if isinstance(typ, str):
454 if isinstance(typ, str):
457 type_module, type_name = typ.rsplit('.', 1)
455 type_module, type_name = typ.rsplit('.', 1)
458 return self.for_type_by_name(type_module, type_name, func)
456 return self.for_type_by_name(type_module, type_name, func)
459
457
460 try:
458 try:
461 oldfunc = self.lookup_by_type(typ)
459 oldfunc = self.lookup_by_type(typ)
462 except KeyError:
460 except KeyError:
463 oldfunc = None
461 oldfunc = None
464
462
465 if func is not None:
463 if func is not None:
466 self.type_printers[typ] = func
464 self.type_printers[typ] = func
467
465
468 return oldfunc
466 return oldfunc
469
467
470 def for_type_by_name(self, type_module, type_name, func=None):
468 def for_type_by_name(self, type_module, type_name, func=None):
471 """Add a format function for a type specified by the full dotted
469 """Add a format function for a type specified by the full dotted
472 module and name of the type, rather than the type of the object.
470 module and name of the type, rather than the type of the object.
473
471
474 Parameters
472 Parameters
475 ----------
473 ----------
476 type_module : str
474 type_module : str
477 The full dotted name of the module the type is defined in, like
475 The full dotted name of the module the type is defined in, like
478 ``numpy``.
476 ``numpy``.
479 type_name : str
477 type_name : str
480 The name of the type (the class name), like ``dtype``
478 The name of the type (the class name), like ``dtype``
481 func : callable
479 func : callable
482 A callable for computing the format data.
480 A callable for computing the format data.
483 `func` will be called with the object to be formatted,
481 `func` will be called with the object to be formatted,
484 and will return the raw data in this formatter's format.
482 and will return the raw data in this formatter's format.
485 Subclasses may use a different call signature for the
483 Subclasses may use a different call signature for the
486 `func` argument.
484 `func` argument.
487
485
488 If `func` is None or unspecified, there will be no change,
486 If `func` is None or unspecified, there will be no change,
489 only returning the current value.
487 only returning the current value.
490
488
491 Returns
489 Returns
492 -------
490 -------
493 oldfunc : callable
491 oldfunc : callable
494 The currently registered callable.
492 The currently registered callable.
495 If you are registering a new formatter,
493 If you are registering a new formatter,
496 this will be the previous value (to enable restoring later).
494 this will be the previous value (to enable restoring later).
497 """
495 """
498 key = (type_module, type_name)
496 key = (type_module, type_name)
499
497
500 try:
498 try:
501 oldfunc = self.lookup_by_type("%s.%s" % key)
499 oldfunc = self.lookup_by_type("%s.%s" % key)
502 except KeyError:
500 except KeyError:
503 oldfunc = None
501 oldfunc = None
504
502
505 if func is not None:
503 if func is not None:
506 self.deferred_printers[key] = func
504 self.deferred_printers[key] = func
507 return oldfunc
505 return oldfunc
508
506
509 def pop(self, typ, default=_raise_key_error):
507 def pop(self, typ, default=_raise_key_error):
510 """Pop a formatter for the given type.
508 """Pop a formatter for the given type.
511
509
512 Parameters
510 Parameters
513 ----------
511 ----------
514 typ : type or '__module__.__name__' string for a type
512 typ : type or '__module__.__name__' string for a type
515 default : object
513 default : object
516 value to be returned if no formatter is registered for typ.
514 value to be returned if no formatter is registered for typ.
517
515
518 Returns
516 Returns
519 -------
517 -------
520 obj : object
518 obj : object
521 The last registered object for the type.
519 The last registered object for the type.
522
520
523 Raises
521 Raises
524 ------
522 ------
525 KeyError if the type is not registered and default is not specified.
523 KeyError if the type is not registered and default is not specified.
526 """
524 """
527
525
528 if isinstance(typ, str):
526 if isinstance(typ, str):
529 typ_key = tuple(typ.rsplit('.',1))
527 typ_key = tuple(typ.rsplit('.',1))
530 if typ_key not in self.deferred_printers:
528 if typ_key not in self.deferred_printers:
531 # We may have it cached in the type map. We will have to
529 # We may have it cached in the type map. We will have to
532 # iterate over all of the types to check.
530 # iterate over all of the types to check.
533 for cls in self.type_printers:
531 for cls in self.type_printers:
534 if _mod_name_key(cls) == typ_key:
532 if _mod_name_key(cls) == typ_key:
535 old = self.type_printers.pop(cls)
533 old = self.type_printers.pop(cls)
536 break
534 break
537 else:
535 else:
538 old = default
536 old = default
539 else:
537 else:
540 old = self.deferred_printers.pop(typ_key)
538 old = self.deferred_printers.pop(typ_key)
541 else:
539 else:
542 if typ in self.type_printers:
540 if typ in self.type_printers:
543 old = self.type_printers.pop(typ)
541 old = self.type_printers.pop(typ)
544 else:
542 else:
545 old = self.deferred_printers.pop(_mod_name_key(typ), default)
543 old = self.deferred_printers.pop(_mod_name_key(typ), default)
546 if old is _raise_key_error:
544 if old is _raise_key_error:
547 raise KeyError("No registered value for {0!r}".format(typ))
545 raise KeyError("No registered value for {0!r}".format(typ))
548 return old
546 return old
549
547
550 def _in_deferred_types(self, cls):
548 def _in_deferred_types(self, cls):
551 """
549 """
552 Check if the given class is specified in the deferred type registry.
550 Check if the given class is specified in the deferred type registry.
553
551
554 Successful matches will be moved to the regular type registry for future use.
552 Successful matches will be moved to the regular type registry for future use.
555 """
553 """
556 mod = getattr(cls, '__module__', None)
554 mod = getattr(cls, '__module__', None)
557 name = getattr(cls, '__name__', None)
555 name = getattr(cls, '__name__', None)
558 key = (mod, name)
556 key = (mod, name)
559 if key in self.deferred_printers:
557 if key in self.deferred_printers:
560 # Move the printer over to the regular registry.
558 # Move the printer over to the regular registry.
561 printer = self.deferred_printers.pop(key)
559 printer = self.deferred_printers.pop(key)
562 self.type_printers[cls] = printer
560 self.type_printers[cls] = printer
563 return True
561 return True
564 return False
562 return False
565
563
566
564
567 class PlainTextFormatter(BaseFormatter):
565 class PlainTextFormatter(BaseFormatter):
568 """The default pretty-printer.
566 """The default pretty-printer.
569
567
570 This uses :mod:`IPython.lib.pretty` to compute the format data of
568 This uses :mod:`IPython.lib.pretty` to compute the format data of
571 the object. If the object cannot be pretty printed, :func:`repr` is used.
569 the object. If the object cannot be pretty printed, :func:`repr` is used.
572 See the documentation of :mod:`IPython.lib.pretty` for details on
570 See the documentation of :mod:`IPython.lib.pretty` for details on
573 how to write pretty printers. Here is a simple example::
571 how to write pretty printers. Here is a simple example::
574
572
575 def dtype_pprinter(obj, p, cycle):
573 def dtype_pprinter(obj, p, cycle):
576 if cycle:
574 if cycle:
577 return p.text('dtype(...)')
575 return p.text('dtype(...)')
578 if hasattr(obj, 'fields'):
576 if hasattr(obj, 'fields'):
579 if obj.fields is None:
577 if obj.fields is None:
580 p.text(repr(obj))
578 p.text(repr(obj))
581 else:
579 else:
582 p.begin_group(7, 'dtype([')
580 p.begin_group(7, 'dtype([')
583 for i, field in enumerate(obj.descr):
581 for i, field in enumerate(obj.descr):
584 if i > 0:
582 if i > 0:
585 p.text(',')
583 p.text(',')
586 p.breakable()
584 p.breakable()
587 p.pretty(field)
585 p.pretty(field)
588 p.end_group(7, '])')
586 p.end_group(7, '])')
589 """
587 """
590
588
591 # The format type of data returned.
589 # The format type of data returned.
592 format_type = Unicode('text/plain')
590 format_type = Unicode('text/plain')
593
591
594 # This subclass ignores this attribute as it always need to return
592 # This subclass ignores this attribute as it always need to return
595 # something.
593 # something.
596 enabled = Bool(True).tag(config=False)
594 enabled = Bool(True).tag(config=False)
597
595
598 max_seq_length = Integer(pretty.MAX_SEQ_LENGTH,
596 max_seq_length = Integer(pretty.MAX_SEQ_LENGTH,
599 help="""Truncate large collections (lists, dicts, tuples, sets) to this size.
597 help="""Truncate large collections (lists, dicts, tuples, sets) to this size.
600
598
601 Set to 0 to disable truncation.
599 Set to 0 to disable truncation.
602 """
600 """
603 ).tag(config=True)
601 ).tag(config=True)
604
602
605 # Look for a _repr_pretty_ methods to use for pretty printing.
603 # Look for a _repr_pretty_ methods to use for pretty printing.
606 print_method = ObjectName('_repr_pretty_')
604 print_method = ObjectName('_repr_pretty_')
607
605
608 # Whether to pretty-print or not.
606 # Whether to pretty-print or not.
609 pprint = Bool(True).tag(config=True)
607 pprint = Bool(True).tag(config=True)
610
608
611 # Whether to be verbose or not.
609 # Whether to be verbose or not.
612 verbose = Bool(False).tag(config=True)
610 verbose = Bool(False).tag(config=True)
613
611
614 # The maximum width.
612 # The maximum width.
615 max_width = Integer(79).tag(config=True)
613 max_width = Integer(79).tag(config=True)
616
614
617 # The newline character.
615 # The newline character.
618 newline = Unicode('\n').tag(config=True)
616 newline = Unicode('\n').tag(config=True)
619
617
620 # format-string for pprinting floats
618 # format-string for pprinting floats
621 float_format = Unicode('%r')
619 float_format = Unicode('%r')
622 # setter for float precision, either int or direct format-string
620 # setter for float precision, either int or direct format-string
623 float_precision = CUnicode('').tag(config=True)
621 float_precision = CUnicode('').tag(config=True)
624
622
625 @observe('float_precision')
623 @observe('float_precision')
626 def _float_precision_changed(self, change):
624 def _float_precision_changed(self, change):
627 """float_precision changed, set float_format accordingly.
625 """float_precision changed, set float_format accordingly.
628
626
629 float_precision can be set by int or str.
627 float_precision can be set by int or str.
630 This will set float_format, after interpreting input.
628 This will set float_format, after interpreting input.
631 If numpy has been imported, numpy print precision will also be set.
629 If numpy has been imported, numpy print precision will also be set.
632
630
633 integer `n` sets format to '%.nf', otherwise, format set directly.
631 integer `n` sets format to '%.nf', otherwise, format set directly.
634
632
635 An empty string returns to defaults (repr for float, 8 for numpy).
633 An empty string returns to defaults (repr for float, 8 for numpy).
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
643 fmt = new
640 fmt = new
644 try:
641 try:
645 fmt%3.14159
642 fmt%3.14159
646 except Exception as e:
643 except Exception as e:
647 raise ValueError("Precision must be int or format string, not %r"%new) from e
644 raise ValueError("Precision must be int or format string, not %r"%new) from e
648 elif new:
645 elif new:
649 # otherwise, should be an int
646 # otherwise, should be an int
650 try:
647 try:
651 i = int(new)
648 i = int(new)
652 assert i >= 0
649 assert i >= 0
653 except ValueError as e:
650 except ValueError as e:
654 raise ValueError("Precision must be int or format string, not %r"%new) from e
651 raise ValueError("Precision must be int or format string, not %r"%new) from e
655 except AssertionError as e:
652 except AssertionError as e:
656 raise ValueError("int precision must be non-negative, not %r"%i) from e
653 raise ValueError("int precision must be non-negative, not %r"%i) from e
657
654
658 fmt = '%%.%if'%i
655 fmt = '%%.%if'%i
659 if 'numpy' in sys.modules:
656 if 'numpy' in sys.modules:
660 # set numpy precision if it has been imported
657 # set numpy precision if it has been imported
661 import numpy
658 import numpy
662 numpy.set_printoptions(precision=i)
659 numpy.set_printoptions(precision=i)
663 else:
660 else:
664 # default back to repr
661 # default back to repr
665 fmt = '%r'
662 fmt = '%r'
666 if 'numpy' in sys.modules:
663 if 'numpy' in sys.modules:
667 import numpy
664 import numpy
668 # numpy default is 8
665 # numpy default is 8
669 numpy.set_printoptions(precision=8)
666 numpy.set_printoptions(precision=8)
670 self.float_format = fmt
667 self.float_format = fmt
671
668
672 # Use the default pretty printers from IPython.lib.pretty.
669 # Use the default pretty printers from IPython.lib.pretty.
673 @default('singleton_printers')
670 @default('singleton_printers')
674 def _singleton_printers_default(self):
671 def _singleton_printers_default(self):
675 return pretty._singleton_pprinters.copy()
672 return pretty._singleton_pprinters.copy()
676
673
677 @default('type_printers')
674 @default('type_printers')
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')
684 def _deferred_printers_default(self):
686 def _deferred_printers_default(self):
685 return pretty._deferred_type_pprinters.copy()
687 return pretty._deferred_type_pprinters.copy()
686
688
687 #### FormatterABC interface ####
689 #### FormatterABC interface ####
688
690
689 @catch_format_error
691 @catch_format_error
690 def __call__(self, obj):
692 def __call__(self, obj):
691 """Compute the pretty representation of the object."""
693 """Compute the pretty representation of the object."""
692 if not self.pprint:
694 if not self.pprint:
693 return repr(obj)
695 return repr(obj)
694 else:
696 else:
695 stream = StringIO()
697 stream = StringIO()
696 printer = pretty.RepresentationPrinter(stream, self.verbose,
698 printer = pretty.RepresentationPrinter(stream, self.verbose,
697 self.max_width, self.newline,
699 self.max_width, self.newline,
698 max_seq_length=self.max_seq_length,
700 max_seq_length=self.max_seq_length,
699 singleton_pprinters=self.singleton_printers,
701 singleton_pprinters=self.singleton_printers,
700 type_pprinters=self.type_printers,
702 type_pprinters=self.type_printers,
701 deferred_pprinters=self.deferred_printers)
703 deferred_pprinters=self.deferred_printers)
702 printer.pretty(obj)
704 printer.pretty(obj)
703 printer.flush()
705 printer.flush()
704 return stream.getvalue()
706 return stream.getvalue()
705
707
706
708
707 class HTMLFormatter(BaseFormatter):
709 class HTMLFormatter(BaseFormatter):
708 """An HTML formatter.
710 """An HTML formatter.
709
711
710 To define the callables that compute the HTML representation of your
712 To define the callables that compute the HTML representation of your
711 objects, define a :meth:`_repr_html_` method or use the :meth:`for_type`
713 objects, define a :meth:`_repr_html_` method or use the :meth:`for_type`
712 or :meth:`for_type_by_name` methods to register functions that handle
714 or :meth:`for_type_by_name` methods to register functions that handle
713 this.
715 this.
714
716
715 The return value of this formatter should be a valid HTML snippet that
717 The return value of this formatter should be a valid HTML snippet that
716 could be injected into an existing DOM. It should *not* include the
718 could be injected into an existing DOM. It should *not* include the
717 ```<html>`` or ```<body>`` tags.
719 ```<html>`` or ```<body>`` tags.
718 """
720 """
719 format_type = Unicode('text/html')
721 format_type = Unicode('text/html')
720
722
721 print_method = ObjectName('_repr_html_')
723 print_method = ObjectName('_repr_html_')
722
724
723
725
724 class MarkdownFormatter(BaseFormatter):
726 class MarkdownFormatter(BaseFormatter):
725 """A Markdown formatter.
727 """A Markdown formatter.
726
728
727 To define the callables that compute the Markdown representation of your
729 To define the callables that compute the Markdown representation of your
728 objects, define a :meth:`_repr_markdown_` method or use the :meth:`for_type`
730 objects, define a :meth:`_repr_markdown_` method or use the :meth:`for_type`
729 or :meth:`for_type_by_name` methods to register functions that handle
731 or :meth:`for_type_by_name` methods to register functions that handle
730 this.
732 this.
731
733
732 The return value of this formatter should be a valid Markdown.
734 The return value of this formatter should be a valid Markdown.
733 """
735 """
734 format_type = Unicode('text/markdown')
736 format_type = Unicode('text/markdown')
735
737
736 print_method = ObjectName('_repr_markdown_')
738 print_method = ObjectName('_repr_markdown_')
737
739
738 class SVGFormatter(BaseFormatter):
740 class SVGFormatter(BaseFormatter):
739 """An SVG formatter.
741 """An SVG formatter.
740
742
741 To define the callables that compute the SVG representation of your
743 To define the callables that compute the SVG representation of your
742 objects, define a :meth:`_repr_svg_` method or use the :meth:`for_type`
744 objects, define a :meth:`_repr_svg_` method or use the :meth:`for_type`
743 or :meth:`for_type_by_name` methods to register functions that handle
745 or :meth:`for_type_by_name` methods to register functions that handle
744 this.
746 this.
745
747
746 The return value of this formatter should be valid SVG enclosed in
748 The return value of this formatter should be valid SVG enclosed in
747 ```<svg>``` tags, that could be injected into an existing DOM. It should
749 ```<svg>``` tags, that could be injected into an existing DOM. It should
748 *not* include the ```<html>`` or ```<body>`` tags.
750 *not* include the ```<html>`` or ```<body>`` tags.
749 """
751 """
750 format_type = Unicode('image/svg+xml')
752 format_type = Unicode('image/svg+xml')
751
753
752 print_method = ObjectName('_repr_svg_')
754 print_method = ObjectName('_repr_svg_')
753
755
754
756
755 class PNGFormatter(BaseFormatter):
757 class PNGFormatter(BaseFormatter):
756 """A PNG formatter.
758 """A PNG formatter.
757
759
758 To define the callables that compute the PNG representation of your
760 To define the callables that compute the PNG representation of your
759 objects, define a :meth:`_repr_png_` method or use the :meth:`for_type`
761 objects, define a :meth:`_repr_png_` method or use the :meth:`for_type`
760 or :meth:`for_type_by_name` methods to register functions that handle
762 or :meth:`for_type_by_name` methods to register functions that handle
761 this.
763 this.
762
764
763 The return value of this formatter should be raw PNG data, *not*
765 The return value of this formatter should be raw PNG data, *not*
764 base64 encoded.
766 base64 encoded.
765 """
767 """
766 format_type = Unicode('image/png')
768 format_type = Unicode('image/png')
767
769
768 print_method = ObjectName('_repr_png_')
770 print_method = ObjectName('_repr_png_')
769
771
770 _return_type = (bytes, str)
772 _return_type = (bytes, str)
771
773
772
774
773 class JPEGFormatter(BaseFormatter):
775 class JPEGFormatter(BaseFormatter):
774 """A JPEG formatter.
776 """A JPEG formatter.
775
777
776 To define the callables that compute the JPEG representation of your
778 To define the callables that compute the JPEG representation of your
777 objects, define a :meth:`_repr_jpeg_` method or use the :meth:`for_type`
779 objects, define a :meth:`_repr_jpeg_` method or use the :meth:`for_type`
778 or :meth:`for_type_by_name` methods to register functions that handle
780 or :meth:`for_type_by_name` methods to register functions that handle
779 this.
781 this.
780
782
781 The return value of this formatter should be raw JPEG data, *not*
783 The return value of this formatter should be raw JPEG data, *not*
782 base64 encoded.
784 base64 encoded.
783 """
785 """
784 format_type = Unicode('image/jpeg')
786 format_type = Unicode('image/jpeg')
785
787
786 print_method = ObjectName('_repr_jpeg_')
788 print_method = ObjectName('_repr_jpeg_')
787
789
788 _return_type = (bytes, str)
790 _return_type = (bytes, str)
789
791
790
792
791 class LatexFormatter(BaseFormatter):
793 class LatexFormatter(BaseFormatter):
792 """A LaTeX formatter.
794 """A LaTeX formatter.
793
795
794 To define the callables that compute the LaTeX representation of your
796 To define the callables that compute the LaTeX representation of your
795 objects, define a :meth:`_repr_latex_` method or use the :meth:`for_type`
797 objects, define a :meth:`_repr_latex_` method or use the :meth:`for_type`
796 or :meth:`for_type_by_name` methods to register functions that handle
798 or :meth:`for_type_by_name` methods to register functions that handle
797 this.
799 this.
798
800
799 The return value of this formatter should be a valid LaTeX equation,
801 The return value of this formatter should be a valid LaTeX equation,
800 enclosed in either ```$```, ```$$``` or another LaTeX equation
802 enclosed in either ```$```, ```$$``` or another LaTeX equation
801 environment.
803 environment.
802 """
804 """
803 format_type = Unicode('text/latex')
805 format_type = Unicode('text/latex')
804
806
805 print_method = ObjectName('_repr_latex_')
807 print_method = ObjectName('_repr_latex_')
806
808
807
809
808 class JSONFormatter(BaseFormatter):
810 class JSONFormatter(BaseFormatter):
809 """A JSON string formatter.
811 """A JSON string formatter.
810
812
811 To define the callables that compute the JSONable representation of
813 To define the callables that compute the JSONable representation of
812 your objects, define a :meth:`_repr_json_` method or use the :meth:`for_type`
814 your objects, define a :meth:`_repr_json_` method or use the :meth:`for_type`
813 or :meth:`for_type_by_name` methods to register functions that handle
815 or :meth:`for_type_by_name` methods to register functions that handle
814 this.
816 this.
815
817
816 The return value of this formatter should be a JSONable list or dict.
818 The return value of this formatter should be a JSONable list or dict.
817 JSON scalars (None, number, string) are not allowed, only dict or list containers.
819 JSON scalars (None, number, string) are not allowed, only dict or list containers.
818 """
820 """
819 format_type = Unicode('application/json')
821 format_type = Unicode('application/json')
820 _return_type = (list, dict)
822 _return_type = (list, dict)
821
823
822 print_method = ObjectName('_repr_json_')
824 print_method = ObjectName('_repr_json_')
823
825
824 def _check_return(self, r, obj):
826 def _check_return(self, r, obj):
825 """Check that a return value is appropriate
827 """Check that a return value is appropriate
826
828
827 Return the value if so, None otherwise, warning if invalid.
829 Return the value if so, None otherwise, warning if invalid.
828 """
830 """
829 if r is None:
831 if r is None:
830 return
832 return
831 md = None
833 md = None
832 if isinstance(r, tuple):
834 if isinstance(r, tuple):
833 # unpack data, metadata tuple for type checking on first element
835 # unpack data, metadata tuple for type checking on first element
834 r, md = r
836 r, md = r
835
837
836 # handle deprecated JSON-as-string form from IPython < 3
838 # handle deprecated JSON-as-string form from IPython < 3
837 if isinstance(r, str):
839 if isinstance(r, str):
838 warnings.warn("JSON expects JSONable list/dict containers, not JSON strings",
840 warnings.warn("JSON expects JSONable list/dict containers, not JSON strings",
839 FormatterWarning)
841 FormatterWarning)
840 r = json.loads(r)
842 r = json.loads(r)
841
843
842 if md is not None:
844 if md is not None:
843 # put the tuple back together
845 # put the tuple back together
844 r = (r, md)
846 r = (r, md)
845 return super(JSONFormatter, self)._check_return(r, obj)
847 return super(JSONFormatter, self)._check_return(r, obj)
846
848
847
849
848 class JavascriptFormatter(BaseFormatter):
850 class JavascriptFormatter(BaseFormatter):
849 """A Javascript formatter.
851 """A Javascript formatter.
850
852
851 To define the callables that compute the Javascript representation of
853 To define the callables that compute the Javascript representation of
852 your objects, define a :meth:`_repr_javascript_` method or use the
854 your objects, define a :meth:`_repr_javascript_` method or use the
853 :meth:`for_type` or :meth:`for_type_by_name` methods to register functions
855 :meth:`for_type` or :meth:`for_type_by_name` methods to register functions
854 that handle this.
856 that handle this.
855
857
856 The return value of this formatter should be valid Javascript code and
858 The return value of this formatter should be valid Javascript code and
857 should *not* be enclosed in ```<script>``` tags.
859 should *not* be enclosed in ```<script>``` tags.
858 """
860 """
859 format_type = Unicode('application/javascript')
861 format_type = Unicode('application/javascript')
860
862
861 print_method = ObjectName('_repr_javascript_')
863 print_method = ObjectName('_repr_javascript_')
862
864
863
865
864 class PDFFormatter(BaseFormatter):
866 class PDFFormatter(BaseFormatter):
865 """A PDF formatter.
867 """A PDF formatter.
866
868
867 To define the callables that compute the PDF representation of your
869 To define the callables that compute the PDF representation of your
868 objects, define a :meth:`_repr_pdf_` method or use the :meth:`for_type`
870 objects, define a :meth:`_repr_pdf_` method or use the :meth:`for_type`
869 or :meth:`for_type_by_name` methods to register functions that handle
871 or :meth:`for_type_by_name` methods to register functions that handle
870 this.
872 this.
871
873
872 The return value of this formatter should be raw PDF data, *not*
874 The return value of this formatter should be raw PDF data, *not*
873 base64 encoded.
875 base64 encoded.
874 """
876 """
875 format_type = Unicode('application/pdf')
877 format_type = Unicode('application/pdf')
876
878
877 print_method = ObjectName('_repr_pdf_')
879 print_method = ObjectName('_repr_pdf_')
878
880
879 _return_type = (bytes, str)
881 _return_type = (bytes, str)
880
882
881 class IPythonDisplayFormatter(BaseFormatter):
883 class IPythonDisplayFormatter(BaseFormatter):
882 """An escape-hatch Formatter for objects that know how to display themselves.
884 """An escape-hatch Formatter for objects that know how to display themselves.
883
885
884 To define the callables that compute the representation of your
886 To define the callables that compute the representation of your
885 objects, define a :meth:`_ipython_display_` method or use the :meth:`for_type`
887 objects, define a :meth:`_ipython_display_` method or use the :meth:`for_type`
886 or :meth:`for_type_by_name` methods to register functions that handle
888 or :meth:`for_type_by_name` methods to register functions that handle
887 this. Unlike mime-type displays, this method should not return anything,
889 this. Unlike mime-type displays, this method should not return anything,
888 instead calling any appropriate display methods itself.
890 instead calling any appropriate display methods itself.
889
891
890 This display formatter has highest priority.
892 This display formatter has highest priority.
891 If it fires, no other display formatter will be called.
893 If it fires, no other display formatter will be called.
892
894
893 Prior to IPython 6.1, `_ipython_display_` was the only way to display custom mime-types
895 Prior to IPython 6.1, `_ipython_display_` was the only way to display custom mime-types
894 without registering a new Formatter.
896 without registering a new Formatter.
895
897
896 IPython 6.1 introduces `_repr_mimebundle_` for displaying custom mime-types,
898 IPython 6.1 introduces `_repr_mimebundle_` for displaying custom mime-types,
897 so `_ipython_display_` should only be used for objects that require unusual
899 so `_ipython_display_` should only be used for objects that require unusual
898 display patterns, such as multiple display calls.
900 display patterns, such as multiple display calls.
899 """
901 """
900 print_method = ObjectName('_ipython_display_')
902 print_method = ObjectName('_ipython_display_')
901 _return_type = (type(None), bool)
903 _return_type = (type(None), bool)
902
904
903 @catch_format_error
905 @catch_format_error
904 def __call__(self, obj):
906 def __call__(self, obj):
905 """Compute the format for an object."""
907 """Compute the format for an object."""
906 if self.enabled:
908 if self.enabled:
907 # lookup registered printer
909 # lookup registered printer
908 try:
910 try:
909 printer = self.lookup(obj)
911 printer = self.lookup(obj)
910 except KeyError:
912 except KeyError:
911 pass
913 pass
912 else:
914 else:
913 printer(obj)
915 printer(obj)
914 return True
916 return True
915 # Finally look for special method names
917 # Finally look for special method names
916 method = get_real_method(obj, self.print_method)
918 method = get_real_method(obj, self.print_method)
917 if method is not None:
919 if method is not None:
918 method()
920 method()
919 return True
921 return True
920
922
921
923
922 class MimeBundleFormatter(BaseFormatter):
924 class MimeBundleFormatter(BaseFormatter):
923 """A Formatter for arbitrary mime-types.
925 """A Formatter for arbitrary mime-types.
924
926
925 Unlike other `_repr_<mimetype>_` methods,
927 Unlike other `_repr_<mimetype>_` methods,
926 `_repr_mimebundle_` should return mime-bundle data,
928 `_repr_mimebundle_` should return mime-bundle data,
927 either the mime-keyed `data` dictionary or the tuple `(data, metadata)`.
929 either the mime-keyed `data` dictionary or the tuple `(data, metadata)`.
928 Any mime-type is valid.
930 Any mime-type is valid.
929
931
930 To define the callables that compute the mime-bundle representation of your
932 To define the callables that compute the mime-bundle representation of your
931 objects, define a :meth:`_repr_mimebundle_` method or use the :meth:`for_type`
933 objects, define a :meth:`_repr_mimebundle_` method or use the :meth:`for_type`
932 or :meth:`for_type_by_name` methods to register functions that handle
934 or :meth:`for_type_by_name` methods to register functions that handle
933 this.
935 this.
934
936
935 .. versionadded:: 6.1
937 .. versionadded:: 6.1
936 """
938 """
937 print_method = ObjectName('_repr_mimebundle_')
939 print_method = ObjectName('_repr_mimebundle_')
938 _return_type = dict
940 _return_type = dict
939
941
940 def _check_return(self, r, obj):
942 def _check_return(self, r, obj):
941 r = super(MimeBundleFormatter, self)._check_return(r, obj)
943 r = super(MimeBundleFormatter, self)._check_return(r, obj)
942 # always return (data, metadata):
944 # always return (data, metadata):
943 if r is None:
945 if r is None:
944 return {}, {}
946 return {}, {}
945 if not isinstance(r, tuple):
947 if not isinstance(r, tuple):
946 return r, {}
948 return r, {}
947 return r
949 return r
948
950
949 @catch_format_error
951 @catch_format_error
950 def __call__(self, obj, include=None, exclude=None):
952 def __call__(self, obj, include=None, exclude=None):
951 """Compute the format for an object.
953 """Compute the format for an object.
952
954
953 Identical to parent's method but we pass extra parameters to the method.
955 Identical to parent's method but we pass extra parameters to the method.
954
956
955 Unlike other _repr_*_ `_repr_mimebundle_` should allow extra kwargs, in
957 Unlike other _repr_*_ `_repr_mimebundle_` should allow extra kwargs, in
956 particular `include` and `exclude`.
958 particular `include` and `exclude`.
957 """
959 """
958 if self.enabled:
960 if self.enabled:
959 # lookup registered printer
961 # lookup registered printer
960 try:
962 try:
961 printer = self.lookup(obj)
963 printer = self.lookup(obj)
962 except KeyError:
964 except KeyError:
963 pass
965 pass
964 else:
966 else:
965 return printer(obj)
967 return printer(obj)
966 # Finally look for special method names
968 # Finally look for special method names
967 method = get_real_method(obj, self.print_method)
969 method = get_real_method(obj, self.print_method)
968
970
969 if method is not None:
971 if method is not None:
970 return method(include=include, exclude=exclude)
972 return method(include=include, exclude=exclude)
971 return None
973 return None
972 else:
974 else:
973 return None
975 return None
974
976
975
977
976 FormatterABC.register(BaseFormatter)
978 FormatterABC.register(BaseFormatter)
977 FormatterABC.register(PlainTextFormatter)
979 FormatterABC.register(PlainTextFormatter)
978 FormatterABC.register(HTMLFormatter)
980 FormatterABC.register(HTMLFormatter)
979 FormatterABC.register(MarkdownFormatter)
981 FormatterABC.register(MarkdownFormatter)
980 FormatterABC.register(SVGFormatter)
982 FormatterABC.register(SVGFormatter)
981 FormatterABC.register(PNGFormatter)
983 FormatterABC.register(PNGFormatter)
982 FormatterABC.register(PDFFormatter)
984 FormatterABC.register(PDFFormatter)
983 FormatterABC.register(JPEGFormatter)
985 FormatterABC.register(JPEGFormatter)
984 FormatterABC.register(LatexFormatter)
986 FormatterABC.register(LatexFormatter)
985 FormatterABC.register(JSONFormatter)
987 FormatterABC.register(JSONFormatter)
986 FormatterABC.register(JavascriptFormatter)
988 FormatterABC.register(JavascriptFormatter)
987 FormatterABC.register(IPythonDisplayFormatter)
989 FormatterABC.register(IPythonDisplayFormatter)
988 FormatterABC.register(MimeBundleFormatter)
990 FormatterABC.register(MimeBundleFormatter)
989
991
990
992
991 def format_display_data(obj, include=None, exclude=None):
993 def format_display_data(obj, include=None, exclude=None):
992 """Return a format data dict for an object.
994 """Return a format data dict for an object.
993
995
994 By default all format types will be computed.
996 By default all format types will be computed.
995
997
996 Parameters
998 Parameters
997 ----------
999 ----------
998 obj : object
1000 obj : object
999 The Python object whose format data will be computed.
1001 The Python object whose format data will be computed.
1000
1002
1001 Returns
1003 Returns
1002 -------
1004 -------
1003 format_dict : dict
1005 format_dict : dict
1004 A dictionary of key/value pairs, one or each format that was
1006 A dictionary of key/value pairs, one or each format that was
1005 generated for the object. The keys are the format types, which
1007 generated for the object. The keys are the format types, which
1006 will usually be MIME type strings and the values and JSON'able
1008 will usually be MIME type strings and the values and JSON'able
1007 data structure containing the raw data for the representation in
1009 data structure containing the raw data for the representation in
1008 that format.
1010 that format.
1009 include : list or tuple, optional
1011 include : list or tuple, optional
1010 A list of format type strings (MIME types) to include in the
1012 A list of format type strings (MIME types) to include in the
1011 format data dict. If this is set *only* the format types included
1013 format data dict. If this is set *only* the format types included
1012 in this list will be computed.
1014 in this list will be computed.
1013 exclude : list or tuple, optional
1015 exclude : list or tuple, optional
1014 A list of format type string (MIME types) to exclude in the format
1016 A list of format type string (MIME types) to exclude in the format
1015 data dict. If this is set all format types will be computed,
1017 data dict. If this is set all format types will be computed,
1016 except for those included in this argument.
1018 except for those included in this argument.
1017 """
1019 """
1018 from .interactiveshell import InteractiveShell
1020 from .interactiveshell import InteractiveShell
1019
1021
1020 return InteractiveShell.instance().display_formatter.format(
1022 return InteractiveShell.instance().display_formatter.format(
1021 obj,
1023 obj,
1022 include,
1024 include,
1023 exclude
1025 exclude
1024 )
1026 )
@@ -1,897 +1,907 b''
1 """ History related magics and functionality """
1 """ History related magics and functionality """
2
2
3 # Copyright (c) IPython Development Team.
3 # Copyright (c) IPython Development Team.
4 # Distributed under the terms of the Modified BSD License.
4 # Distributed under the terms of the Modified BSD License.
5
5
6
6
7 import atexit
7 import atexit
8 import datetime
8 import datetime
9 from pathlib import Path
9 from pathlib import Path
10 import re
10 import re
11 import sqlite3
11 import sqlite3
12 import threading
12 import threading
13
13
14 from traitlets.config.configurable import LoggingConfigurable
14 from traitlets.config.configurable import LoggingConfigurable
15 from decorator import decorator
15 from decorator import decorator
16 from IPython.utils.decorators import undoc
16 from IPython.utils.decorators import undoc
17 from IPython.paths import locate_profile
17 from IPython.paths import locate_profile
18 from traitlets import (
18 from traitlets import (
19 Any,
19 Any,
20 Bool,
20 Bool,
21 Dict,
21 Dict,
22 Instance,
22 Instance,
23 Integer,
23 Integer,
24 List,
24 List,
25 Unicode,
25 Unicode,
26 Union,
26 Union,
27 TraitError,
27 TraitError,
28 default,
28 default,
29 observe,
29 observe,
30 )
30 )
31
31
32 #-----------------------------------------------------------------------------
32 #-----------------------------------------------------------------------------
33 # Classes and functions
33 # Classes and functions
34 #-----------------------------------------------------------------------------
34 #-----------------------------------------------------------------------------
35
35
36 @undoc
36 @undoc
37 class DummyDB(object):
37 class DummyDB(object):
38 """Dummy DB that will act as a black hole for history.
38 """Dummy DB that will act as a black hole for history.
39
39
40 Only used in the absence of sqlite"""
40 Only used in the absence of sqlite"""
41 def execute(*args, **kwargs):
41 def execute(*args, **kwargs):
42 return []
42 return []
43
43
44 def commit(self, *args, **kwargs):
44 def commit(self, *args, **kwargs):
45 pass
45 pass
46
46
47 def __enter__(self, *args, **kwargs):
47 def __enter__(self, *args, **kwargs):
48 pass
48 pass
49
49
50 def __exit__(self, *args, **kwargs):
50 def __exit__(self, *args, **kwargs):
51 pass
51 pass
52
52
53
53
54 @decorator
54 @decorator
55 def only_when_enabled(f, self, *a, **kw):
55 def only_when_enabled(f, self, *a, **kw):
56 """Decorator: return an empty list in the absence of sqlite."""
56 """Decorator: return an empty list in the absence of sqlite."""
57 if not self.enabled:
57 if not self.enabled:
58 return []
58 return []
59 else:
59 else:
60 return f(self, *a, **kw)
60 return f(self, *a, **kw)
61
61
62
62
63 # use 16kB as threshold for whether a corrupt history db should be saved
63 # use 16kB as threshold for whether a corrupt history db should be saved
64 # that should be at least 100 entries or so
64 # that should be at least 100 entries or so
65 _SAVE_DB_SIZE = 16384
65 _SAVE_DB_SIZE = 16384
66
66
67 @decorator
67 @decorator
68 def catch_corrupt_db(f, self, *a, **kw):
68 def catch_corrupt_db(f, self, *a, **kw):
69 """A decorator which wraps HistoryAccessor method calls to catch errors from
69 """A decorator which wraps HistoryAccessor method calls to catch errors from
70 a corrupt SQLite database, move the old database out of the way, and create
70 a corrupt SQLite database, move the old database out of the way, and create
71 a new one.
71 a new one.
72
72
73 We avoid clobbering larger databases because this may be triggered due to filesystem issues,
73 We avoid clobbering larger databases because this may be triggered due to filesystem issues,
74 not just a corrupt file.
74 not just a corrupt file.
75 """
75 """
76 try:
76 try:
77 return f(self, *a, **kw)
77 return f(self, *a, **kw)
78 except (sqlite3.DatabaseError, sqlite3.OperationalError) as e:
78 except (sqlite3.DatabaseError, sqlite3.OperationalError) as e:
79 self._corrupt_db_counter += 1
79 self._corrupt_db_counter += 1
80 self.log.error("Failed to open SQLite history %s (%s).", self.hist_file, e)
80 self.log.error("Failed to open SQLite history %s (%s).", self.hist_file, e)
81 if self.hist_file != ':memory:':
81 if self.hist_file != ':memory:':
82 if self._corrupt_db_counter > self._corrupt_db_limit:
82 if self._corrupt_db_counter > self._corrupt_db_limit:
83 self.hist_file = ':memory:'
83 self.hist_file = ':memory:'
84 self.log.error("Failed to load history too many times, history will not be saved.")
84 self.log.error("Failed to load history too many times, history will not be saved.")
85 elif self.hist_file.is_file():
85 elif self.hist_file.is_file():
86 # move the file out of the way
86 # move the file out of the way
87 base = str(self.hist_file.parent / self.hist_file.stem)
87 base = str(self.hist_file.parent / self.hist_file.stem)
88 ext = self.hist_file.suffix
88 ext = self.hist_file.suffix
89 size = self.hist_file.stat().st_size
89 size = self.hist_file.stat().st_size
90 if size >= _SAVE_DB_SIZE:
90 if size >= _SAVE_DB_SIZE:
91 # if there's significant content, avoid clobbering
91 # if there's significant content, avoid clobbering
92 now = datetime.datetime.now().isoformat().replace(':', '.')
92 now = datetime.datetime.now().isoformat().replace(':', '.')
93 newpath = base + '-corrupt-' + now + ext
93 newpath = base + '-corrupt-' + now + ext
94 # don't clobber previous corrupt backups
94 # don't clobber previous corrupt backups
95 for i in range(100):
95 for i in range(100):
96 if not Path(newpath).exists():
96 if not Path(newpath).exists():
97 break
97 break
98 else:
98 else:
99 newpath = base + '-corrupt-' + now + (u'-%i' % i) + ext
99 newpath = base + '-corrupt-' + now + (u'-%i' % i) + ext
100 else:
100 else:
101 # not much content, possibly empty; don't worry about clobbering
101 # not much content, possibly empty; don't worry about clobbering
102 # maybe we should just delete it?
102 # maybe we should just delete it?
103 newpath = base + '-corrupt' + ext
103 newpath = base + '-corrupt' + ext
104 self.hist_file.rename(newpath)
104 self.hist_file.rename(newpath)
105 self.log.error("History file was moved to %s and a new file created.", newpath)
105 self.log.error("History file was moved to %s and a new file created.", newpath)
106 self.init_db()
106 self.init_db()
107 return []
107 return []
108 else:
108 else:
109 # Failed with :memory:, something serious is wrong
109 # Failed with :memory:, something serious is wrong
110 raise
110 raise
111
111
112
112
113 class HistoryAccessorBase(LoggingConfigurable):
113 class HistoryAccessorBase(LoggingConfigurable):
114 """An abstract class for History Accessors """
114 """An abstract class for History Accessors """
115
115
116 def get_tail(self, n=10, raw=True, output=False, include_latest=False):
116 def get_tail(self, n=10, raw=True, output=False, include_latest=False):
117 raise NotImplementedError
117 raise NotImplementedError
118
118
119 def search(self, pattern="*", raw=True, search_raw=True,
119 def search(self, pattern="*", raw=True, search_raw=True,
120 output=False, n=None, unique=False):
120 output=False, n=None, unique=False):
121 raise NotImplementedError
121 raise NotImplementedError
122
122
123 def get_range(self, session, start=1, stop=None, raw=True,output=False):
123 def get_range(self, session, start=1, stop=None, raw=True,output=False):
124 raise NotImplementedError
124 raise NotImplementedError
125
125
126 def get_range_by_str(self, rangestr, raw=True, output=False):
126 def get_range_by_str(self, rangestr, raw=True, output=False):
127 raise NotImplementedError
127 raise NotImplementedError
128
128
129
129
130 class HistoryAccessor(HistoryAccessorBase):
130 class HistoryAccessor(HistoryAccessorBase):
131 """Access the history database without adding to it.
131 """Access the history database without adding to it.
132
132
133 This is intended for use by standalone history tools. IPython shells use
133 This is intended for use by standalone history tools. IPython shells use
134 HistoryManager, below, which is a subclass of this."""
134 HistoryManager, below, which is a subclass of this."""
135
135
136 # counter for init_db retries, so we don't keep trying over and over
136 # counter for init_db retries, so we don't keep trying over and over
137 _corrupt_db_counter = 0
137 _corrupt_db_counter = 0
138 # after two failures, fallback on :memory:
138 # after two failures, fallback on :memory:
139 _corrupt_db_limit = 2
139 _corrupt_db_limit = 2
140
140
141 # String holding the path to the history file
141 # String holding the path to the history file
142 hist_file = Union(
142 hist_file = Union(
143 [Instance(Path), Unicode()],
143 [Instance(Path), Unicode()],
144 help="""Path to file to use for SQLite history database.
144 help="""Path to file to use for SQLite history database.
145
145
146 By default, IPython will put the history database in the IPython
146 By default, IPython will put the history database in the IPython
147 profile directory. If you would rather share one history among
147 profile directory. If you would rather share one history among
148 profiles, you can set this value in each, so that they are consistent.
148 profiles, you can set this value in each, so that they are consistent.
149
149
150 Due to an issue with fcntl, SQLite is known to misbehave on some NFS
150 Due to an issue with fcntl, SQLite is known to misbehave on some NFS
151 mounts. If you see IPython hanging, try setting this to something on a
151 mounts. If you see IPython hanging, try setting this to something on a
152 local disk, e.g::
152 local disk, e.g::
153
153
154 ipython --HistoryManager.hist_file=/tmp/ipython_hist.sqlite
154 ipython --HistoryManager.hist_file=/tmp/ipython_hist.sqlite
155
155
156 you can also use the specific value `:memory:` (including the colon
156 you can also use the specific value `:memory:` (including the colon
157 at both end but not the back ticks), to avoid creating an history file.
157 at both end but not the back ticks), to avoid creating an history file.
158
158
159 """,
159 """,
160 ).tag(config=True)
160 ).tag(config=True)
161
161
162 enabled = Bool(True,
162 enabled = Bool(True,
163 help="""enable the SQLite history
163 help="""enable the SQLite history
164
164
165 set enabled=False to disable the SQLite history,
165 set enabled=False to disable the SQLite history,
166 in which case there will be no stored history, no SQLite connection,
166 in which case there will be no stored history, no SQLite connection,
167 and no background saving thread. This may be necessary in some
167 and no background saving thread. This may be necessary in some
168 threaded environments where IPython is embedded.
168 threaded environments where IPython is embedded.
169 """
169 """
170 ).tag(config=True)
170 ).tag(config=True)
171
171
172 connection_options = Dict(
172 connection_options = Dict(
173 help="""Options for configuring the SQLite connection
173 help="""Options for configuring the SQLite connection
174
174
175 These options are passed as keyword args to sqlite3.connect
175 These options are passed as keyword args to sqlite3.connect
176 when establishing database connections.
176 when establishing database connections.
177 """
177 """
178 ).tag(config=True)
178 ).tag(config=True)
179
179
180 # The SQLite database
180 # The SQLite database
181 db = Any()
181 db = Any()
182 @observe('db')
182 @observe('db')
183 def _db_changed(self, change):
183 def _db_changed(self, change):
184 """validate the db, since it can be an Instance of two different types"""
184 """validate the db, since it can be an Instance of two different types"""
185 new = change['new']
185 new = change['new']
186 connection_types = (DummyDB, sqlite3.Connection)
186 connection_types = (DummyDB, sqlite3.Connection)
187 if not isinstance(new, connection_types):
187 if not isinstance(new, connection_types):
188 msg = "%s.db must be sqlite3 Connection or DummyDB, not %r" % \
188 msg = "%s.db must be sqlite3 Connection or DummyDB, not %r" % \
189 (self.__class__.__name__, new)
189 (self.__class__.__name__, new)
190 raise TraitError(msg)
190 raise TraitError(msg)
191
191
192 def __init__(self, profile="default", hist_file="", **traits):
192 def __init__(self, profile="default", hist_file="", **traits):
193 """Create a new history accessor.
193 """Create a new history accessor.
194
194
195 Parameters
195 Parameters
196 ----------
196 ----------
197 profile : str
197 profile : str
198 The name of the profile from which to open history.
198 The name of the profile from which to open history.
199 hist_file : str
199 hist_file : str
200 Path to an SQLite history database stored by IPython. If specified,
200 Path to an SQLite history database stored by IPython. If specified,
201 hist_file overrides profile.
201 hist_file overrides profile.
202 config : :class:`~traitlets.config.loader.Config`
202 config : :class:`~traitlets.config.loader.Config`
203 Config object. hist_file can also be set through this.
203 Config object. hist_file can also be set through this.
204 """
204 """
205 # We need a pointer back to the shell for various tasks.
205 # We need a pointer back to the shell for various tasks.
206 super(HistoryAccessor, self).__init__(**traits)
206 super(HistoryAccessor, self).__init__(**traits)
207 # defer setting hist_file from kwarg until after init,
207 # defer setting hist_file from kwarg until after init,
208 # otherwise the default kwarg value would clobber any value
208 # otherwise the default kwarg value would clobber any value
209 # set by config
209 # set by config
210 if hist_file:
210 if hist_file:
211 self.hist_file = hist_file
211 self.hist_file = hist_file
212
212
213 try:
213 try:
214 self.hist_file
214 self.hist_file
215 except TraitError:
215 except TraitError:
216 # No one has set the hist_file, yet.
216 # No one has set the hist_file, yet.
217 self.hist_file = self._get_hist_file_name(profile)
217 self.hist_file = self._get_hist_file_name(profile)
218
218
219 self.init_db()
219 self.init_db()
220
220
221 def _get_hist_file_name(self, profile='default'):
221 def _get_hist_file_name(self, profile='default'):
222 """Find the history file for the given profile name.
222 """Find the history file for the given profile name.
223
223
224 This is overridden by the HistoryManager subclass, to use the shell's
224 This is overridden by the HistoryManager subclass, to use the shell's
225 active profile.
225 active profile.
226
226
227 Parameters
227 Parameters
228 ----------
228 ----------
229 profile : str
229 profile : str
230 The name of a profile which has a history file.
230 The name of a profile which has a history file.
231 """
231 """
232 return Path(locate_profile(profile)) / "history.sqlite"
232 return Path(locate_profile(profile)) / "history.sqlite"
233
233
234 @catch_corrupt_db
234 @catch_corrupt_db
235 def init_db(self):
235 def init_db(self):
236 """Connect to the database, and create tables if necessary."""
236 """Connect to the database, and create tables if necessary."""
237 if not self.enabled:
237 if not self.enabled:
238 self.db = DummyDB()
238 self.db = DummyDB()
239 return
239 return
240
240
241 # use detect_types so that timestamps return datetime objects
241 # use detect_types so that timestamps return datetime objects
242 kwargs = dict(detect_types=sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES)
242 kwargs = dict(detect_types=sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES)
243 kwargs.update(self.connection_options)
243 kwargs.update(self.connection_options)
244 self.db = sqlite3.connect(str(self.hist_file), **kwargs)
244 self.db = sqlite3.connect(str(self.hist_file), **kwargs)
245 self.db.execute("""CREATE TABLE IF NOT EXISTS sessions (session integer
245 self.db.execute("""CREATE TABLE IF NOT EXISTS sessions (session integer
246 primary key autoincrement, start timestamp,
246 primary key autoincrement, start timestamp,
247 end timestamp, num_cmds integer, remark text)""")
247 end timestamp, num_cmds integer, remark text)""")
248 self.db.execute("""CREATE TABLE IF NOT EXISTS history
248 self.db.execute("""CREATE TABLE IF NOT EXISTS history
249 (session integer, line integer, source text, source_raw text,
249 (session integer, line integer, source text, source_raw text,
250 PRIMARY KEY (session, line))""")
250 PRIMARY KEY (session, line))""")
251 # Output history is optional, but ensure the table's there so it can be
251 # Output history is optional, but ensure the table's there so it can be
252 # enabled later.
252 # enabled later.
253 self.db.execute("""CREATE TABLE IF NOT EXISTS output_history
253 self.db.execute("""CREATE TABLE IF NOT EXISTS output_history
254 (session integer, line integer, output text,
254 (session integer, line integer, output text,
255 PRIMARY KEY (session, line))""")
255 PRIMARY KEY (session, line))""")
256 self.db.commit()
256 self.db.commit()
257 # success! reset corrupt db count
257 # success! reset corrupt db count
258 self._corrupt_db_counter = 0
258 self._corrupt_db_counter = 0
259
259
260 def writeout_cache(self):
260 def writeout_cache(self):
261 """Overridden by HistoryManager to dump the cache before certain
261 """Overridden by HistoryManager to dump the cache before certain
262 database lookups."""
262 database lookups."""
263 pass
263 pass
264
264
265 ## -------------------------------
265 ## -------------------------------
266 ## Methods for retrieving history:
266 ## Methods for retrieving history:
267 ## -------------------------------
267 ## -------------------------------
268 def _run_sql(self, sql, params, raw=True, output=False):
268 def _run_sql(self, sql, params, raw=True, output=False):
269 """Prepares and runs an SQL query for the history database.
269 """Prepares and runs an SQL query for the history database.
270
270
271 Parameters
271 Parameters
272 ----------
272 ----------
273 sql : str
273 sql : str
274 Any filtering expressions to go after SELECT ... FROM ...
274 Any filtering expressions to go after SELECT ... FROM ...
275 params : tuple
275 params : tuple
276 Parameters passed to the SQL query (to replace "?")
276 Parameters passed to the SQL query (to replace "?")
277 raw, output : bool
277 raw, output : bool
278 See :meth:`get_range`
278 See :meth:`get_range`
279
279
280 Returns
280 Returns
281 -------
281 -------
282 Tuples as :meth:`get_range`
282 Tuples as :meth:`get_range`
283 """
283 """
284 toget = 'source_raw' if raw else 'source'
284 toget = 'source_raw' if raw else 'source'
285 sqlfrom = "history"
285 sqlfrom = "history"
286 if output:
286 if output:
287 sqlfrom = "history LEFT JOIN output_history USING (session, line)"
287 sqlfrom = "history LEFT JOIN output_history USING (session, line)"
288 toget = "history.%s, output_history.output" % toget
288 toget = "history.%s, output_history.output" % toget
289 cur = self.db.execute("SELECT session, line, %s FROM %s " %\
289 cur = self.db.execute("SELECT session, line, %s FROM %s " %\
290 (toget, sqlfrom) + sql, params)
290 (toget, sqlfrom) + sql, params)
291 if output: # Regroup into 3-tuples, and parse JSON
291 if output: # Regroup into 3-tuples, and parse JSON
292 return ((ses, lin, (inp, out)) for ses, lin, inp, out in cur)
292 return ((ses, lin, (inp, out)) for ses, lin, inp, out in cur)
293 return cur
293 return cur
294
294
295 @only_when_enabled
295 @only_when_enabled
296 @catch_corrupt_db
296 @catch_corrupt_db
297 def get_session_info(self, session):
297 def get_session_info(self, session):
298 """Get info about a session.
298 """Get info about a session.
299
299
300 Parameters
300 Parameters
301 ----------
301 ----------
302
302
303 session : int
303 session : int
304 Session number to retrieve.
304 Session number to retrieve.
305
305
306 Returns
306 Returns
307 -------
307 -------
308
308
309 session_id : int
309 session_id : int
310 Session ID number
310 Session ID number
311 start : datetime
311 start : datetime
312 Timestamp for the start of the session.
312 Timestamp for the start of the session.
313 end : datetime
313 end : datetime
314 Timestamp for the end of the session, or None if IPython crashed.
314 Timestamp for the end of the session, or None if IPython crashed.
315 num_cmds : int
315 num_cmds : int
316 Number of commands run, or None if IPython crashed.
316 Number of commands run, or None if IPython crashed.
317 remark : unicode
317 remark : unicode
318 A manually set description.
318 A manually set description.
319 """
319 """
320 query = "SELECT * from sessions where session == ?"
320 query = "SELECT * from sessions where session == ?"
321 return self.db.execute(query, (session,)).fetchone()
321 return self.db.execute(query, (session,)).fetchone()
322
322
323 @catch_corrupt_db
323 @catch_corrupt_db
324 def get_last_session_id(self):
324 def get_last_session_id(self):
325 """Get the last session ID currently in the database.
325 """Get the last session ID currently in the database.
326
326
327 Within IPython, this should be the same as the value stored in
327 Within IPython, this should be the same as the value stored in
328 :attr:`HistoryManager.session_number`.
328 :attr:`HistoryManager.session_number`.
329 """
329 """
330 for record in self.get_tail(n=1, include_latest=True):
330 for record in self.get_tail(n=1, include_latest=True):
331 return record[0]
331 return record[0]
332
332
333 @catch_corrupt_db
333 @catch_corrupt_db
334 def get_tail(self, n=10, raw=True, output=False, include_latest=False):
334 def get_tail(self, n=10, raw=True, output=False, include_latest=False):
335 """Get the last n lines from the history database.
335 """Get the last n lines from the history database.
336
336
337 Parameters
337 Parameters
338 ----------
338 ----------
339 n : int
339 n : int
340 The number of lines to get
340 The number of lines to get
341 raw, output : bool
341 raw, output : bool
342 See :meth:`get_range`
342 See :meth:`get_range`
343 include_latest : bool
343 include_latest : bool
344 If False (default), n+1 lines are fetched, and the latest one
344 If False (default), n+1 lines are fetched, and the latest one
345 is discarded. This is intended to be used where the function
345 is discarded. This is intended to be used where the function
346 is called by a user command, which it should not return.
346 is called by a user command, which it should not return.
347
347
348 Returns
348 Returns
349 -------
349 -------
350 Tuples as :meth:`get_range`
350 Tuples as :meth:`get_range`
351 """
351 """
352 self.writeout_cache()
352 self.writeout_cache()
353 if not include_latest:
353 if not include_latest:
354 n += 1
354 n += 1
355 cur = self._run_sql("ORDER BY session DESC, line DESC LIMIT ?",
355 cur = self._run_sql("ORDER BY session DESC, line DESC LIMIT ?",
356 (n,), raw=raw, output=output)
356 (n,), raw=raw, output=output)
357 if not include_latest:
357 if not include_latest:
358 return reversed(list(cur)[1:])
358 return reversed(list(cur)[1:])
359 return reversed(list(cur))
359 return reversed(list(cur))
360
360
361 @catch_corrupt_db
361 @catch_corrupt_db
362 def search(self, pattern="*", raw=True, search_raw=True,
362 def search(self, pattern="*", raw=True, search_raw=True,
363 output=False, n=None, unique=False):
363 output=False, n=None, unique=False):
364 """Search the database using unix glob-style matching (wildcards
364 """Search the database using unix glob-style matching (wildcards
365 * and ?).
365 * and ?).
366
366
367 Parameters
367 Parameters
368 ----------
368 ----------
369 pattern : str
369 pattern : str
370 The wildcarded pattern to match when searching
370 The wildcarded pattern to match when searching
371 search_raw : bool
371 search_raw : bool
372 If True, search the raw input, otherwise, the parsed input
372 If True, search the raw input, otherwise, the parsed input
373 raw, output : bool
373 raw, output : bool
374 See :meth:`get_range`
374 See :meth:`get_range`
375 n : None or int
375 n : None or int
376 If an integer is given, it defines the limit of
376 If an integer is given, it defines the limit of
377 returned entries.
377 returned entries.
378 unique : bool
378 unique : bool
379 When it is true, return only unique entries.
379 When it is true, return only unique entries.
380
380
381 Returns
381 Returns
382 -------
382 -------
383 Tuples as :meth:`get_range`
383 Tuples as :meth:`get_range`
384 """
384 """
385 tosearch = "source_raw" if search_raw else "source"
385 tosearch = "source_raw" if search_raw else "source"
386 if output:
386 if output:
387 tosearch = "history." + tosearch
387 tosearch = "history." + tosearch
388 self.writeout_cache()
388 self.writeout_cache()
389 sqlform = "WHERE %s GLOB ?" % tosearch
389 sqlform = "WHERE %s GLOB ?" % tosearch
390 params = (pattern,)
390 params = (pattern,)
391 if unique:
391 if unique:
392 sqlform += ' GROUP BY {0}'.format(tosearch)
392 sqlform += ' GROUP BY {0}'.format(tosearch)
393 if n is not None:
393 if n is not None:
394 sqlform += " ORDER BY session DESC, line DESC LIMIT ?"
394 sqlform += " ORDER BY session DESC, line DESC LIMIT ?"
395 params += (n,)
395 params += (n,)
396 elif unique:
396 elif unique:
397 sqlform += " ORDER BY session, line"
397 sqlform += " ORDER BY session, line"
398 cur = self._run_sql(sqlform, params, raw=raw, output=output)
398 cur = self._run_sql(sqlform, params, raw=raw, output=output)
399 if n is not None:
399 if n is not None:
400 return reversed(list(cur))
400 return reversed(list(cur))
401 return cur
401 return cur
402
402
403 @catch_corrupt_db
403 @catch_corrupt_db
404 def get_range(self, session, start=1, stop=None, raw=True,output=False):
404 def get_range(self, session, start=1, stop=None, raw=True,output=False):
405 """Retrieve input by session.
405 """Retrieve input by session.
406
406
407 Parameters
407 Parameters
408 ----------
408 ----------
409 session : int
409 session : int
410 Session number to retrieve.
410 Session number to retrieve.
411 start : int
411 start : int
412 First line to retrieve.
412 First line to retrieve.
413 stop : int
413 stop : int
414 End of line range (excluded from output itself). If None, retrieve
414 End of line range (excluded from output itself). If None, retrieve
415 to the end of the session.
415 to the end of the session.
416 raw : bool
416 raw : bool
417 If True, return untranslated input
417 If True, return untranslated input
418 output : bool
418 output : bool
419 If True, attempt to include output. This will be 'real' Python
419 If True, attempt to include output. This will be 'real' Python
420 objects for the current session, or text reprs from previous
420 objects for the current session, or text reprs from previous
421 sessions if db_log_output was enabled at the time. Where no output
421 sessions if db_log_output was enabled at the time. Where no output
422 is found, None is used.
422 is found, None is used.
423
423
424 Returns
424 Returns
425 -------
425 -------
426 entries
426 entries
427 An iterator over the desired lines. Each line is a 3-tuple, either
427 An iterator over the desired lines. Each line is a 3-tuple, either
428 (session, line, input) if output is False, or
428 (session, line, input) if output is False, or
429 (session, line, (input, output)) if output is True.
429 (session, line, (input, output)) if output is True.
430 """
430 """
431 if stop:
431 if stop:
432 lineclause = "line >= ? AND line < ?"
432 lineclause = "line >= ? AND line < ?"
433 params = (session, start, stop)
433 params = (session, start, stop)
434 else:
434 else:
435 lineclause = "line>=?"
435 lineclause = "line>=?"
436 params = (session, start)
436 params = (session, start)
437
437
438 return self._run_sql("WHERE session==? AND %s" % lineclause,
438 return self._run_sql("WHERE session==? AND %s" % lineclause,
439 params, raw=raw, output=output)
439 params, raw=raw, output=output)
440
440
441 def get_range_by_str(self, rangestr, raw=True, output=False):
441 def get_range_by_str(self, rangestr, raw=True, output=False):
442 """Get lines of history from a string of ranges, as used by magic
442 """Get lines of history from a string of ranges, as used by magic
443 commands %hist, %save, %macro, etc.
443 commands %hist, %save, %macro, etc.
444
444
445 Parameters
445 Parameters
446 ----------
446 ----------
447 rangestr : str
447 rangestr : str
448 A string specifying ranges, e.g. "5 ~2/1-4". See
448 A string specifying ranges, e.g. "5 ~2/1-4". If empty string is used,
449 :func:`magic_history` for full details.
449 this will return everything from current session's history.
450
451 See the documentation of :func:`%history` for the full details.
452
450 raw, output : bool
453 raw, output : bool
451 As :meth:`get_range`
454 As :meth:`get_range`
452
455
453 Returns
456 Returns
454 -------
457 -------
455 Tuples as :meth:`get_range`
458 Tuples as :meth:`get_range`
456 """
459 """
457 for sess, s, e in extract_hist_ranges(rangestr):
460 for sess, s, e in extract_hist_ranges(rangestr):
458 for line in self.get_range(sess, s, e, raw=raw, output=output):
461 for line in self.get_range(sess, s, e, raw=raw, output=output):
459 yield line
462 yield line
460
463
461
464
462 class HistoryManager(HistoryAccessor):
465 class HistoryManager(HistoryAccessor):
463 """A class to organize all history-related functionality in one place.
466 """A class to organize all history-related functionality in one place.
464 """
467 """
465 # Public interface
468 # Public interface
466
469
467 # An instance of the IPython shell we are attached to
470 # An instance of the IPython shell we are attached to
468 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC',
471 shell = Instance('IPython.core.interactiveshell.InteractiveShellABC',
469 allow_none=True)
472 allow_none=True)
470 # Lists to hold processed and raw history. These start with a blank entry
473 # Lists to hold processed and raw history. These start with a blank entry
471 # so that we can index them starting from 1
474 # so that we can index them starting from 1
472 input_hist_parsed = List([""])
475 input_hist_parsed = List([""])
473 input_hist_raw = List([""])
476 input_hist_raw = List([""])
474 # A list of directories visited during session
477 # A list of directories visited during session
475 dir_hist = List()
478 dir_hist = List()
476 @default('dir_hist')
479 @default('dir_hist')
477 def _dir_hist_default(self):
480 def _dir_hist_default(self):
478 try:
481 try:
479 return [Path.cwd()]
482 return [Path.cwd()]
480 except OSError:
483 except OSError:
481 return []
484 return []
482
485
483 # A dict of output history, keyed with ints from the shell's
486 # A dict of output history, keyed with ints from the shell's
484 # execution count.
487 # execution count.
485 output_hist = Dict()
488 output_hist = Dict()
486 # The text/plain repr of outputs.
489 # The text/plain repr of outputs.
487 output_hist_reprs = Dict()
490 output_hist_reprs = Dict()
488
491
489 # The number of the current session in the history database
492 # The number of the current session in the history database
490 session_number = Integer()
493 session_number = Integer()
491
494
492 db_log_output = Bool(False,
495 db_log_output = Bool(False,
493 help="Should the history database include output? (default: no)"
496 help="Should the history database include output? (default: no)"
494 ).tag(config=True)
497 ).tag(config=True)
495 db_cache_size = Integer(0,
498 db_cache_size = Integer(0,
496 help="Write to database every x commands (higher values save disk access & power).\n"
499 help="Write to database every x commands (higher values save disk access & power).\n"
497 "Values of 1 or less effectively disable caching."
500 "Values of 1 or less effectively disable caching."
498 ).tag(config=True)
501 ).tag(config=True)
499 # The input and output caches
502 # The input and output caches
500 db_input_cache = List()
503 db_input_cache = List()
501 db_output_cache = List()
504 db_output_cache = List()
502
505
503 # History saving in separate thread
506 # History saving in separate thread
504 save_thread = Instance('IPython.core.history.HistorySavingThread',
507 save_thread = Instance('IPython.core.history.HistorySavingThread',
505 allow_none=True)
508 allow_none=True)
506 save_flag = Instance(threading.Event, allow_none=True)
509 save_flag = Instance(threading.Event, allow_none=True)
507
510
508 # Private interface
511 # Private interface
509 # Variables used to store the three last inputs from the user. On each new
512 # Variables used to store the three last inputs from the user. On each new
510 # history update, we populate the user's namespace with these, shifted as
513 # history update, we populate the user's namespace with these, shifted as
511 # necessary.
514 # necessary.
512 _i00 = Unicode(u'')
515 _i00 = Unicode(u'')
513 _i = Unicode(u'')
516 _i = Unicode(u'')
514 _ii = Unicode(u'')
517 _ii = Unicode(u'')
515 _iii = Unicode(u'')
518 _iii = Unicode(u'')
516
519
517 # A regex matching all forms of the exit command, so that we don't store
520 # A regex matching all forms of the exit command, so that we don't store
518 # them in the history (it's annoying to rewind the first entry and land on
521 # them in the history (it's annoying to rewind the first entry and land on
519 # an exit call).
522 # an exit call).
520 _exit_re = re.compile(r"(exit|quit)(\s*\(.*\))?$")
523 _exit_re = re.compile(r"(exit|quit)(\s*\(.*\))?$")
521
524
522 def __init__(self, shell=None, config=None, **traits):
525 def __init__(self, shell=None, config=None, **traits):
523 """Create a new history manager associated with a shell instance.
526 """Create a new history manager associated with a shell instance.
524 """
527 """
525 # We need a pointer back to the shell for various tasks.
528 # We need a pointer back to the shell for various tasks.
526 super(HistoryManager, self).__init__(shell=shell, config=config,
529 super(HistoryManager, self).__init__(shell=shell, config=config,
527 **traits)
530 **traits)
528 self.save_flag = threading.Event()
531 self.save_flag = threading.Event()
529 self.db_input_cache_lock = threading.Lock()
532 self.db_input_cache_lock = threading.Lock()
530 self.db_output_cache_lock = threading.Lock()
533 self.db_output_cache_lock = threading.Lock()
531
534
532 try:
535 try:
533 self.new_session()
536 self.new_session()
534 except sqlite3.OperationalError:
537 except sqlite3.OperationalError:
535 self.log.error("Failed to create history session in %s. History will not be saved.",
538 self.log.error("Failed to create history session in %s. History will not be saved.",
536 self.hist_file, exc_info=True)
539 self.hist_file, exc_info=True)
537 self.hist_file = ':memory:'
540 self.hist_file = ':memory:'
538
541
539 if self.enabled and self.hist_file != ':memory:':
542 if self.enabled and self.hist_file != ':memory:':
540 self.save_thread = HistorySavingThread(self)
543 self.save_thread = HistorySavingThread(self)
541 self.save_thread.start()
544 self.save_thread.start()
542
545
543 def _get_hist_file_name(self, profile=None):
546 def _get_hist_file_name(self, profile=None):
544 """Get default history file name based on the Shell's profile.
547 """Get default history file name based on the Shell's profile.
545
548
546 The profile parameter is ignored, but must exist for compatibility with
549 The profile parameter is ignored, but must exist for compatibility with
547 the parent class."""
550 the parent class."""
548 profile_dir = self.shell.profile_dir.location
551 profile_dir = self.shell.profile_dir.location
549 return Path(profile_dir) / "history.sqlite"
552 return Path(profile_dir) / "history.sqlite"
550
553
551 @only_when_enabled
554 @only_when_enabled
552 def new_session(self, conn=None):
555 def new_session(self, conn=None):
553 """Get a new session number."""
556 """Get a new session number."""
554 if conn is None:
557 if conn is None:
555 conn = self.db
558 conn = self.db
556
559
557 with conn:
560 with conn:
558 cur = conn.execute("""INSERT INTO sessions VALUES (NULL, ?, NULL,
561 cur = conn.execute("""INSERT INTO sessions VALUES (NULL, ?, NULL,
559 NULL, "") """, (datetime.datetime.now(),))
562 NULL, "") """, (datetime.datetime.now(),))
560 self.session_number = cur.lastrowid
563 self.session_number = cur.lastrowid
561
564
562 def end_session(self):
565 def end_session(self):
563 """Close the database session, filling in the end time and line count."""
566 """Close the database session, filling in the end time and line count."""
564 self.writeout_cache()
567 self.writeout_cache()
565 with self.db:
568 with self.db:
566 self.db.execute("""UPDATE sessions SET end=?, num_cmds=? WHERE
569 self.db.execute("""UPDATE sessions SET end=?, num_cmds=? WHERE
567 session==?""", (datetime.datetime.now(),
570 session==?""", (datetime.datetime.now(),
568 len(self.input_hist_parsed)-1, self.session_number))
571 len(self.input_hist_parsed)-1, self.session_number))
569 self.session_number = 0
572 self.session_number = 0
570
573
571 def name_session(self, name):
574 def name_session(self, name):
572 """Give the current session a name in the history database."""
575 """Give the current session a name in the history database."""
573 with self.db:
576 with self.db:
574 self.db.execute("UPDATE sessions SET remark=? WHERE session==?",
577 self.db.execute("UPDATE sessions SET remark=? WHERE session==?",
575 (name, self.session_number))
578 (name, self.session_number))
576
579
577 def reset(self, new_session=True):
580 def reset(self, new_session=True):
578 """Clear the session history, releasing all object references, and
581 """Clear the session history, releasing all object references, and
579 optionally open a new session."""
582 optionally open a new session."""
580 self.output_hist.clear()
583 self.output_hist.clear()
581 # The directory history can't be completely empty
584 # The directory history can't be completely empty
582 self.dir_hist[:] = [Path.cwd()]
585 self.dir_hist[:] = [Path.cwd()]
583
586
584 if new_session:
587 if new_session:
585 if self.session_number:
588 if self.session_number:
586 self.end_session()
589 self.end_session()
587 self.input_hist_parsed[:] = [""]
590 self.input_hist_parsed[:] = [""]
588 self.input_hist_raw[:] = [""]
591 self.input_hist_raw[:] = [""]
589 self.new_session()
592 self.new_session()
590
593
591 # ------------------------------
594 # ------------------------------
592 # Methods for retrieving history
595 # Methods for retrieving history
593 # ------------------------------
596 # ------------------------------
594 def get_session_info(self, session=0):
597 def get_session_info(self, session=0):
595 """Get info about a session.
598 """Get info about a session.
596
599
597 Parameters
600 Parameters
598 ----------
601 ----------
599
602
600 session : int
603 session : int
601 Session number to retrieve. The current session is 0, and negative
604 Session number to retrieve. The current session is 0, and negative
602 numbers count back from current session, so -1 is the previous session.
605 numbers count back from current session, so -1 is the previous session.
603
606
604 Returns
607 Returns
605 -------
608 -------
606
609
607 session_id : int
610 session_id : int
608 Session ID number
611 Session ID number
609 start : datetime
612 start : datetime
610 Timestamp for the start of the session.
613 Timestamp for the start of the session.
611 end : datetime
614 end : datetime
612 Timestamp for the end of the session, or None if IPython crashed.
615 Timestamp for the end of the session, or None if IPython crashed.
613 num_cmds : int
616 num_cmds : int
614 Number of commands run, or None if IPython crashed.
617 Number of commands run, or None if IPython crashed.
615 remark : unicode
618 remark : unicode
616 A manually set description.
619 A manually set description.
617 """
620 """
618 if session <= 0:
621 if session <= 0:
619 session += self.session_number
622 session += self.session_number
620
623
621 return super(HistoryManager, self).get_session_info(session=session)
624 return super(HistoryManager, self).get_session_info(session=session)
622
625
623 def _get_range_session(self, start=1, stop=None, raw=True, output=False):
626 def _get_range_session(self, start=1, stop=None, raw=True, output=False):
624 """Get input and output history from the current session. Called by
627 """Get input and output history from the current session. Called by
625 get_range, and takes similar parameters."""
628 get_range, and takes similar parameters."""
626 input_hist = self.input_hist_raw if raw else self.input_hist_parsed
629 input_hist = self.input_hist_raw if raw else self.input_hist_parsed
627
630
628 n = len(input_hist)
631 n = len(input_hist)
629 if start < 0:
632 if start < 0:
630 start += n
633 start += n
631 if not stop or (stop > n):
634 if not stop or (stop > n):
632 stop = n
635 stop = n
633 elif stop < 0:
636 elif stop < 0:
634 stop += n
637 stop += n
635
638
636 for i in range(start, stop):
639 for i in range(start, stop):
637 if output:
640 if output:
638 line = (input_hist[i], self.output_hist_reprs.get(i))
641 line = (input_hist[i], self.output_hist_reprs.get(i))
639 else:
642 else:
640 line = input_hist[i]
643 line = input_hist[i]
641 yield (0, i, line)
644 yield (0, i, line)
642
645
643 def get_range(self, session=0, start=1, stop=None, raw=True,output=False):
646 def get_range(self, session=0, start=1, stop=None, raw=True,output=False):
644 """Retrieve input by session.
647 """Retrieve input by session.
645
648
646 Parameters
649 Parameters
647 ----------
650 ----------
648 session : int
651 session : int
649 Session number to retrieve. The current session is 0, and negative
652 Session number to retrieve. The current session is 0, and negative
650 numbers count back from current session, so -1 is previous session.
653 numbers count back from current session, so -1 is previous session.
651 start : int
654 start : int
652 First line to retrieve.
655 First line to retrieve.
653 stop : int
656 stop : int
654 End of line range (excluded from output itself). If None, retrieve
657 End of line range (excluded from output itself). If None, retrieve
655 to the end of the session.
658 to the end of the session.
656 raw : bool
659 raw : bool
657 If True, return untranslated input
660 If True, return untranslated input
658 output : bool
661 output : bool
659 If True, attempt to include output. This will be 'real' Python
662 If True, attempt to include output. This will be 'real' Python
660 objects for the current session, or text reprs from previous
663 objects for the current session, or text reprs from previous
661 sessions if db_log_output was enabled at the time. Where no output
664 sessions if db_log_output was enabled at the time. Where no output
662 is found, None is used.
665 is found, None is used.
663
666
664 Returns
667 Returns
665 -------
668 -------
666 entries
669 entries
667 An iterator over the desired lines. Each line is a 3-tuple, either
670 An iterator over the desired lines. Each line is a 3-tuple, either
668 (session, line, input) if output is False, or
671 (session, line, input) if output is False, or
669 (session, line, (input, output)) if output is True.
672 (session, line, (input, output)) if output is True.
670 """
673 """
671 if session <= 0:
674 if session <= 0:
672 session += self.session_number
675 session += self.session_number
673 if session==self.session_number: # Current session
676 if session==self.session_number: # Current session
674 return self._get_range_session(start, stop, raw, output)
677 return self._get_range_session(start, stop, raw, output)
675 return super(HistoryManager, self).get_range(session, start, stop, raw,
678 return super(HistoryManager, self).get_range(session, start, stop, raw,
676 output)
679 output)
677
680
678 ## ----------------------------
681 ## ----------------------------
679 ## Methods for storing history:
682 ## Methods for storing history:
680 ## ----------------------------
683 ## ----------------------------
681 def store_inputs(self, line_num, source, source_raw=None):
684 def store_inputs(self, line_num, source, source_raw=None):
682 """Store source and raw input in history and create input cache
685 """Store source and raw input in history and create input cache
683 variables ``_i*``.
686 variables ``_i*``.
684
687
685 Parameters
688 Parameters
686 ----------
689 ----------
687 line_num : int
690 line_num : int
688 The prompt number of this input.
691 The prompt number of this input.
689
692
690 source : str
693 source : str
691 Python input.
694 Python input.
692
695
693 source_raw : str, optional
696 source_raw : str, optional
694 If given, this is the raw input without any IPython transformations
697 If given, this is the raw input without any IPython transformations
695 applied to it. If not given, ``source`` is used.
698 applied to it. If not given, ``source`` is used.
696 """
699 """
697 if source_raw is None:
700 if source_raw is None:
698 source_raw = source
701 source_raw = source
699 source = source.rstrip('\n')
702 source = source.rstrip('\n')
700 source_raw = source_raw.rstrip('\n')
703 source_raw = source_raw.rstrip('\n')
701
704
702 # do not store exit/quit commands
705 # do not store exit/quit commands
703 if self._exit_re.match(source_raw.strip()):
706 if self._exit_re.match(source_raw.strip()):
704 return
707 return
705
708
706 self.input_hist_parsed.append(source)
709 self.input_hist_parsed.append(source)
707 self.input_hist_raw.append(source_raw)
710 self.input_hist_raw.append(source_raw)
708
711
709 with self.db_input_cache_lock:
712 with self.db_input_cache_lock:
710 self.db_input_cache.append((line_num, source, source_raw))
713 self.db_input_cache.append((line_num, source, source_raw))
711 # Trigger to flush cache and write to DB.
714 # Trigger to flush cache and write to DB.
712 if len(self.db_input_cache) >= self.db_cache_size:
715 if len(self.db_input_cache) >= self.db_cache_size:
713 self.save_flag.set()
716 self.save_flag.set()
714
717
715 # update the auto _i variables
718 # update the auto _i variables
716 self._iii = self._ii
719 self._iii = self._ii
717 self._ii = self._i
720 self._ii = self._i
718 self._i = self._i00
721 self._i = self._i00
719 self._i00 = source_raw
722 self._i00 = source_raw
720
723
721 # hackish access to user namespace to create _i1,_i2... dynamically
724 # hackish access to user namespace to create _i1,_i2... dynamically
722 new_i = '_i%s' % line_num
725 new_i = '_i%s' % line_num
723 to_main = {'_i': self._i,
726 to_main = {'_i': self._i,
724 '_ii': self._ii,
727 '_ii': self._ii,
725 '_iii': self._iii,
728 '_iii': self._iii,
726 new_i : self._i00 }
729 new_i : self._i00 }
727
730
728 if self.shell is not None:
731 if self.shell is not None:
729 self.shell.push(to_main, interactive=False)
732 self.shell.push(to_main, interactive=False)
730
733
731 def store_output(self, line_num):
734 def store_output(self, line_num):
732 """If database output logging is enabled, this saves all the
735 """If database output logging is enabled, this saves all the
733 outputs from the indicated prompt number to the database. It's
736 outputs from the indicated prompt number to the database. It's
734 called by run_cell after code has been executed.
737 called by run_cell after code has been executed.
735
738
736 Parameters
739 Parameters
737 ----------
740 ----------
738 line_num : int
741 line_num : int
739 The line number from which to save outputs
742 The line number from which to save outputs
740 """
743 """
741 if (not self.db_log_output) or (line_num not in self.output_hist_reprs):
744 if (not self.db_log_output) or (line_num not in self.output_hist_reprs):
742 return
745 return
743 output = self.output_hist_reprs[line_num]
746 output = self.output_hist_reprs[line_num]
744
747
745 with self.db_output_cache_lock:
748 with self.db_output_cache_lock:
746 self.db_output_cache.append((line_num, output))
749 self.db_output_cache.append((line_num, output))
747 if self.db_cache_size <= 1:
750 if self.db_cache_size <= 1:
748 self.save_flag.set()
751 self.save_flag.set()
749
752
750 def _writeout_input_cache(self, conn):
753 def _writeout_input_cache(self, conn):
751 with conn:
754 with conn:
752 for line in self.db_input_cache:
755 for line in self.db_input_cache:
753 conn.execute("INSERT INTO history VALUES (?, ?, ?, ?)",
756 conn.execute("INSERT INTO history VALUES (?, ?, ?, ?)",
754 (self.session_number,)+line)
757 (self.session_number,)+line)
755
758
756 def _writeout_output_cache(self, conn):
759 def _writeout_output_cache(self, conn):
757 with conn:
760 with conn:
758 for line in self.db_output_cache:
761 for line in self.db_output_cache:
759 conn.execute("INSERT INTO output_history VALUES (?, ?, ?)",
762 conn.execute("INSERT INTO output_history VALUES (?, ?, ?)",
760 (self.session_number,)+line)
763 (self.session_number,)+line)
761
764
762 @only_when_enabled
765 @only_when_enabled
763 def writeout_cache(self, conn=None):
766 def writeout_cache(self, conn=None):
764 """Write any entries in the cache to the database."""
767 """Write any entries in the cache to the database."""
765 if conn is None:
768 if conn is None:
766 conn = self.db
769 conn = self.db
767
770
768 with self.db_input_cache_lock:
771 with self.db_input_cache_lock:
769 try:
772 try:
770 self._writeout_input_cache(conn)
773 self._writeout_input_cache(conn)
771 except sqlite3.IntegrityError:
774 except sqlite3.IntegrityError:
772 self.new_session(conn)
775 self.new_session(conn)
773 print("ERROR! Session/line number was not unique in",
776 print("ERROR! Session/line number was not unique in",
774 "database. History logging moved to new session",
777 "database. History logging moved to new session",
775 self.session_number)
778 self.session_number)
776 try:
779 try:
777 # Try writing to the new session. If this fails, don't
780 # Try writing to the new session. If this fails, don't
778 # recurse
781 # recurse
779 self._writeout_input_cache(conn)
782 self._writeout_input_cache(conn)
780 except sqlite3.IntegrityError:
783 except sqlite3.IntegrityError:
781 pass
784 pass
782 finally:
785 finally:
783 self.db_input_cache = []
786 self.db_input_cache = []
784
787
785 with self.db_output_cache_lock:
788 with self.db_output_cache_lock:
786 try:
789 try:
787 self._writeout_output_cache(conn)
790 self._writeout_output_cache(conn)
788 except sqlite3.IntegrityError:
791 except sqlite3.IntegrityError:
789 print("!! Session/line number for output was not unique",
792 print("!! Session/line number for output was not unique",
790 "in database. Output will not be stored.")
793 "in database. Output will not be stored.")
791 finally:
794 finally:
792 self.db_output_cache = []
795 self.db_output_cache = []
793
796
794
797
795 class HistorySavingThread(threading.Thread):
798 class HistorySavingThread(threading.Thread):
796 """This thread takes care of writing history to the database, so that
799 """This thread takes care of writing history to the database, so that
797 the UI isn't held up while that happens.
800 the UI isn't held up while that happens.
798
801
799 It waits for the HistoryManager's save_flag to be set, then writes out
802 It waits for the HistoryManager's save_flag to be set, then writes out
800 the history cache. The main thread is responsible for setting the flag when
803 the history cache. The main thread is responsible for setting the flag when
801 the cache size reaches a defined threshold."""
804 the cache size reaches a defined threshold."""
802 daemon = True
805 daemon = True
803 stop_now = False
806 stop_now = False
804 enabled = True
807 enabled = True
805 def __init__(self, history_manager):
808 def __init__(self, history_manager):
806 super(HistorySavingThread, self).__init__(name="IPythonHistorySavingThread")
809 super(HistorySavingThread, self).__init__(name="IPythonHistorySavingThread")
807 self.history_manager = history_manager
810 self.history_manager = history_manager
808 self.enabled = history_manager.enabled
811 self.enabled = history_manager.enabled
809 atexit.register(self.stop)
812 atexit.register(self.stop)
810
813
811 @only_when_enabled
814 @only_when_enabled
812 def run(self):
815 def run(self):
813 # We need a separate db connection per thread:
816 # We need a separate db connection per thread:
814 try:
817 try:
815 self.db = sqlite3.connect(
818 self.db = sqlite3.connect(
816 str(self.history_manager.hist_file),
819 str(self.history_manager.hist_file),
817 **self.history_manager.connection_options
820 **self.history_manager.connection_options
818 )
821 )
819 while True:
822 while True:
820 self.history_manager.save_flag.wait()
823 self.history_manager.save_flag.wait()
821 if self.stop_now:
824 if self.stop_now:
822 self.db.close()
825 self.db.close()
823 return
826 return
824 self.history_manager.save_flag.clear()
827 self.history_manager.save_flag.clear()
825 self.history_manager.writeout_cache(self.db)
828 self.history_manager.writeout_cache(self.db)
826 except Exception as e:
829 except Exception as e:
827 print(("The history saving thread hit an unexpected error (%s)."
830 print(("The history saving thread hit an unexpected error (%s)."
828 "History will not be written to the database.") % repr(e))
831 "History will not be written to the database.") % repr(e))
829
832
830 def stop(self):
833 def stop(self):
831 """This can be called from the main thread to safely stop this thread.
834 """This can be called from the main thread to safely stop this thread.
832
835
833 Note that it does not attempt to write out remaining history before
836 Note that it does not attempt to write out remaining history before
834 exiting. That should be done by calling the HistoryManager's
837 exiting. That should be done by calling the HistoryManager's
835 end_session method."""
838 end_session method."""
836 self.stop_now = True
839 self.stop_now = True
837 self.history_manager.save_flag.set()
840 self.history_manager.save_flag.set()
838 self.join()
841 self.join()
839
842
840
843
841 # To match, e.g. ~5/8-~2/3
844 # To match, e.g. ~5/8-~2/3
842 range_re = re.compile(r"""
845 range_re = re.compile(r"""
843 ((?P<startsess>~?\d+)/)?
846 ((?P<startsess>~?\d+)/)?
844 (?P<start>\d+)?
847 (?P<start>\d+)?
845 ((?P<sep>[\-:])
848 ((?P<sep>[\-:])
846 ((?P<endsess>~?\d+)/)?
849 ((?P<endsess>~?\d+)/)?
847 (?P<end>\d+))?
850 (?P<end>\d+))?
848 $""", re.VERBOSE)
851 $""", re.VERBOSE)
849
852
850
853
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:
862 continue
872 continue
863 start = rmatch.group("start")
873 start = rmatch.group("start")
864 if start:
874 if start:
865 start = int(start)
875 start = int(start)
866 end = rmatch.group("end")
876 end = rmatch.group("end")
867 # If no end specified, get (a, a + 1)
877 # If no end specified, get (a, a + 1)
868 end = int(end) if end else start + 1
878 end = int(end) if end else start + 1
869 else: # start not specified
879 else: # start not specified
870 if not rmatch.group('startsess'): # no startsess
880 if not rmatch.group('startsess'): # no startsess
871 continue
881 continue
872 start = 1
882 start = 1
873 end = None # provide the entire session hist
883 end = None # provide the entire session hist
874
884
875 if rmatch.group("sep") == "-": # 1-3 == 1:4 --> [1, 2, 3]
885 if rmatch.group("sep") == "-": # 1-3 == 1:4 --> [1, 2, 3]
876 end += 1
886 end += 1
877 startsess = rmatch.group("startsess") or "0"
887 startsess = rmatch.group("startsess") or "0"
878 endsess = rmatch.group("endsess") or startsess
888 endsess = rmatch.group("endsess") or startsess
879 startsess = int(startsess.replace("~","-"))
889 startsess = int(startsess.replace("~","-"))
880 endsess = int(endsess.replace("~","-"))
890 endsess = int(endsess.replace("~","-"))
881 assert endsess >= startsess, "start session must be earlier than end session"
891 assert endsess >= startsess, "start session must be earlier than end session"
882
892
883 if endsess == startsess:
893 if endsess == startsess:
884 yield (startsess, start, end)
894 yield (startsess, start, end)
885 continue
895 continue
886 # Multiple sessions in one range:
896 # Multiple sessions in one range:
887 yield (startsess, start, None)
897 yield (startsess, start, None)
888 for sess in range(startsess+1, endsess):
898 for sess in range(startsess+1, endsess):
889 yield (sess, 1, None)
899 yield (sess, 1, None)
890 yield (endsess, 1, end)
900 yield (endsess, 1, end)
891
901
892
902
893 def _format_lineno(session, line):
903 def _format_lineno(session, line):
894 """Helper function to format line numbers properly."""
904 """Helper function to format line numbers properly."""
895 if session == 0:
905 if session == 0:
896 return str(line)
906 return str(line)
897 return "%s#%s" % (session, line)
907 return "%s#%s" % (session, line)
@@ -1,729 +1,799 b''
1 """Input transformer machinery to support IPython special syntax.
1 """Input transformer machinery to support IPython special syntax.
2
2
3 This includes the machinery to recognise and transform ``%magic`` commands,
3 This includes the machinery to recognise and transform ``%magic`` commands,
4 ``!system`` commands, ``help?`` querying, prompt stripping, and so forth.
4 ``!system`` commands, ``help?`` querying, prompt stripping, and so forth.
5
5
6 Added: IPython 7.0. Replaces inputsplitter and inputtransformer which were
6 Added: IPython 7.0. Replaces inputsplitter and inputtransformer which were
7 deprecated in 7.0.
7 deprecated in 7.0.
8 """
8 """
9
9
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
17 import warnings
19 import warnings
18
20
19 _indent_re = re.compile(r'^[ \t]+')
21 _indent_re = re.compile(r'^[ \t]+')
20
22
21 def leading_empty_lines(lines):
23 def leading_empty_lines(lines):
22 """Remove leading empty lines
24 """Remove leading empty lines
23
25
24 If the leading lines are empty or contain only whitespace, they will be
26 If the leading lines are empty or contain only whitespace, they will be
25 removed.
27 removed.
26 """
28 """
27 if not lines:
29 if not lines:
28 return lines
30 return lines
29 for i, line in enumerate(lines):
31 for i, line in enumerate(lines):
30 if line and not line.isspace():
32 if line and not line.isspace():
31 return lines[i:]
33 return lines[i:]
32 return lines
34 return lines
33
35
34 def leading_indent(lines):
36 def leading_indent(lines):
35 """Remove leading indentation.
37 """Remove leading indentation.
36
38
37 If the first line starts with a spaces or tabs, the same whitespace will be
39 If the first line starts with a spaces or tabs, the same whitespace will be
38 removed from each following line in the cell.
40 removed from each following line in the cell.
39 """
41 """
40 if not lines:
42 if not lines:
41 return lines
43 return lines
42 m = _indent_re.match(lines[0])
44 m = _indent_re.match(lines[0])
43 if not m:
45 if not m:
44 return lines
46 return lines
45 space = m.group(0)
47 space = m.group(0)
46 n = len(space)
48 n = len(space)
47 return [l[n:] if l.startswith(space) else l
49 return [l[n:] if l.startswith(space) else l
48 for l in lines]
50 for l in lines]
49
51
50 class PromptStripper:
52 class PromptStripper:
51 """Remove matching input prompts from a block of input.
53 """Remove matching input prompts from a block of input.
52
54
53 Parameters
55 Parameters
54 ----------
56 ----------
55 prompt_re : regular expression
57 prompt_re : regular expression
56 A regular expression matching any input prompt (including continuation,
58 A regular expression matching any input prompt (including continuation,
57 e.g. ``...``)
59 e.g. ``...``)
58 initial_re : regular expression, optional
60 initial_re : regular expression, optional
59 A regular expression matching only the initial prompt, but not continuation.
61 A regular expression matching only the initial prompt, but not continuation.
60 If no initial expression is given, prompt_re will be used everywhere.
62 If no initial expression is given, prompt_re will be used everywhere.
61 Used mainly for plain Python prompts (``>>>``), where the continuation prompt
63 Used mainly for plain Python prompts (``>>>``), where the continuation prompt
62 ``...`` is a valid Python expression in Python 3, so shouldn't be stripped.
64 ``...`` is a valid Python expression in Python 3, so shouldn't be stripped.
63
65
64 Notes
66 Notes
65 -----
67 -----
66
68
67 If initial_re and prompt_re differ,
69 If initial_re and prompt_re differ,
68 only initial_re will be tested against the first line.
70 only initial_re will be tested against the first line.
69 If any prompt is found on the first two lines,
71 If any prompt is found on the first two lines,
70 prompts will be stripped from the rest of the block.
72 prompts will be stripped from the rest of the block.
71 """
73 """
72 def __init__(self, prompt_re, initial_re=None):
74 def __init__(self, prompt_re, initial_re=None):
73 self.prompt_re = prompt_re
75 self.prompt_re = prompt_re
74 self.initial_re = initial_re or prompt_re
76 self.initial_re = initial_re or prompt_re
75
77
76 def _strip(self, lines):
78 def _strip(self, lines):
77 return [self.prompt_re.sub('', l, count=1) for l in lines]
79 return [self.prompt_re.sub('', l, count=1) for l in lines]
78
80
79 def __call__(self, lines):
81 def __call__(self, lines):
80 if not lines:
82 if not lines:
81 return lines
83 return lines
82 if self.initial_re.match(lines[0]) or \
84 if self.initial_re.match(lines[0]) or \
83 (len(lines) > 1 and self.prompt_re.match(lines[1])):
85 (len(lines) > 1 and self.prompt_re.match(lines[1])):
84 return self._strip(lines)
86 return self._strip(lines)
85 return lines
87 return lines
86
88
87 classic_prompt = PromptStripper(
89 classic_prompt = PromptStripper(
88 prompt_re=re.compile(r'^(>>>|\.\.\.)( |$)'),
90 prompt_re=re.compile(r'^(>>>|\.\.\.)( |$)'),
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('%%'):
96 return lines
121 return lines
97 if re.match(r'%%\w+\?', lines[0]):
122 if re.match(r'%%\w+\?', lines[0]):
98 # This case will be handled by help_end
123 # This case will be handled by help_end
99 return lines
124 return lines
100 magic_name, _, first_line = lines[0][2:].rstrip().partition(' ')
125 magic_name, _, first_line = lines[0][2:].rstrip().partition(' ')
101 body = ''.join(lines[1:])
126 body = ''.join(lines[1:])
102 return ['get_ipython().run_cell_magic(%r, %r, %r)\n'
127 return ['get_ipython().run_cell_magic(%r, %r, %r)\n'
103 % (magic_name, first_line, body)]
128 % (magic_name, first_line, body)]
104
129
105
130
106 def _find_assign_op(token_line) -> Optional[int]:
131 def _find_assign_op(token_line) -> Optional[int]:
107 """Get the index of the first assignment in the line ('=' not inside brackets)
132 """Get the index of the first assignment in the line ('=' not inside brackets)
108
133
109 Note: We don't try to support multiple special assignment (a = b = %foo)
134 Note: We don't try to support multiple special assignment (a = b = %foo)
110 """
135 """
111 paren_level = 0
136 paren_level = 0
112 for i, ti in enumerate(token_line):
137 for i, ti in enumerate(token_line):
113 s = ti.string
138 s = ti.string
114 if s == '=' and paren_level == 0:
139 if s == '=' and paren_level == 0:
115 return i
140 return i
116 if s in {'(','[','{'}:
141 if s in {'(','[','{'}:
117 paren_level += 1
142 paren_level += 1
118 elif s in {')', ']', '}'}:
143 elif s in {')', ']', '}'}:
119 if paren_level > 0:
144 if paren_level > 0:
120 paren_level -= 1
145 paren_level -= 1
121 return None
146 return None
122
147
123 def find_end_of_continued_line(lines, start_line: int):
148 def find_end_of_continued_line(lines, start_line: int):
124 """Find the last line of a line explicitly extended using backslashes.
149 """Find the last line of a line explicitly extended using backslashes.
125
150
126 Uses 0-indexed line numbers.
151 Uses 0-indexed line numbers.
127 """
152 """
128 end_line = start_line
153 end_line = start_line
129 while lines[end_line].endswith('\\\n'):
154 while lines[end_line].endswith('\\\n'):
130 end_line += 1
155 end_line += 1
131 if end_line >= len(lines):
156 if end_line >= len(lines):
132 break
157 break
133 return end_line
158 return end_line
134
159
135 def assemble_continued_line(lines, start: Tuple[int, int], end_line: int):
160 def assemble_continued_line(lines, start: Tuple[int, int], end_line: int):
136 r"""Assemble a single line from multiple continued line pieces
161 r"""Assemble a single line from multiple continued line pieces
137
162
138 Continued lines are lines ending in ``\``, and the line following the last
163 Continued lines are lines ending in ``\``, and the line following the last
139 ``\`` in the block.
164 ``\`` in the block.
140
165
141 For example, this code continues over multiple lines::
166 For example, this code continues over multiple lines::
142
167
143 if (assign_ix is not None) \
168 if (assign_ix is not None) \
144 and (len(line) >= assign_ix + 2) \
169 and (len(line) >= assign_ix + 2) \
145 and (line[assign_ix+1].string == '%') \
170 and (line[assign_ix+1].string == '%') \
146 and (line[assign_ix+2].type == tokenize.NAME):
171 and (line[assign_ix+2].type == tokenize.NAME):
147
172
148 This statement contains four continued line pieces.
173 This statement contains four continued line pieces.
149 Assembling these pieces into a single line would give::
174 Assembling these pieces into a single line would give::
150
175
151 if (assign_ix is not None) and (len(line) >= assign_ix + 2) and (line[...
176 if (assign_ix is not None) and (len(line) >= assign_ix + 2) and (line[...
152
177
153 This uses 0-indexed line numbers. *start* is (lineno, colno).
178 This uses 0-indexed line numbers. *start* is (lineno, colno).
154
179
155 Used to allow ``%magic`` and ``!system`` commands to be continued over
180 Used to allow ``%magic`` and ``!system`` commands to be continued over
156 multiple lines.
181 multiple lines.
157 """
182 """
158 parts = [lines[start[0]][start[1]:]] + lines[start[0]+1:end_line+1]
183 parts = [lines[start[0]][start[1]:]] + lines[start[0]+1:end_line+1]
159 return ' '.join([p.rstrip()[:-1] for p in parts[:-1]] # Strip backslash+newline
184 return ' '.join([p.rstrip()[:-1] for p in parts[:-1]] # Strip backslash+newline
160 + [parts[-1].rstrip()]) # Strip newline from last line
185 + [parts[-1].rstrip()]) # Strip newline from last line
161
186
162 class TokenTransformBase:
187 class TokenTransformBase:
163 """Base class for transformations which examine tokens.
188 """Base class for transformations which examine tokens.
164
189
165 Special syntax should not be transformed when it occurs inside strings or
190 Special syntax should not be transformed when it occurs inside strings or
166 comments. This is hard to reliably avoid with regexes. The solution is to
191 comments. This is hard to reliably avoid with regexes. The solution is to
167 tokenise the code as Python, and recognise the special syntax in the tokens.
192 tokenise the code as Python, and recognise the special syntax in the tokens.
168
193
169 IPython's special syntax is not valid Python syntax, so tokenising may go
194 IPython's special syntax is not valid Python syntax, so tokenising may go
170 wrong after the special syntax starts. These classes therefore find and
195 wrong after the special syntax starts. These classes therefore find and
171 transform *one* instance of special syntax at a time into regular Python
196 transform *one* instance of special syntax at a time into regular Python
172 syntax. After each transformation, tokens are regenerated to find the next
197 syntax. After each transformation, tokens are regenerated to find the next
173 piece of special syntax.
198 piece of special syntax.
174
199
175 Subclasses need to implement one class method (find)
200 Subclasses need to implement one class method (find)
176 and one regular method (transform).
201 and one regular method (transform).
177
202
178 The priority attribute can select which transformation to apply if multiple
203 The priority attribute can select which transformation to apply if multiple
179 transformers match in the same place. Lower numbers have higher priority.
204 transformers match in the same place. Lower numbers have higher priority.
180 This allows "%magic?" to be turned into a help call rather than a magic call.
205 This allows "%magic?" to be turned into a help call rather than a magic call.
181 """
206 """
182 # Lower numbers -> higher priority (for matches in the same location)
207 # Lower numbers -> higher priority (for matches in the same location)
183 priority = 10
208 priority = 10
184
209
185 def sortby(self):
210 def sortby(self):
186 return self.start_line, self.start_col, self.priority
211 return self.start_line, self.start_col, self.priority
187
212
188 def __init__(self, start):
213 def __init__(self, start):
189 self.start_line = start[0] - 1 # Shift from 1-index to 0-index
214 self.start_line = start[0] - 1 # Shift from 1-index to 0-index
190 self.start_col = start[1]
215 self.start_col = start[1]
191
216
192 @classmethod
217 @classmethod
193 def find(cls, tokens_by_line):
218 def find(cls, tokens_by_line):
194 """Find one instance of special syntax in the provided tokens.
219 """Find one instance of special syntax in the provided tokens.
195
220
196 Tokens are grouped into logical lines for convenience,
221 Tokens are grouped into logical lines for convenience,
197 so it is easy to e.g. look at the first token of each line.
222 so it is easy to e.g. look at the first token of each line.
198 *tokens_by_line* is a list of lists of tokenize.TokenInfo objects.
223 *tokens_by_line* is a list of lists of tokenize.TokenInfo objects.
199
224
200 This should return an instance of its class, pointing to the start
225 This should return an instance of its class, pointing to the start
201 position it has found, or None if it found no match.
226 position it has found, or None if it found no match.
202 """
227 """
203 raise NotImplementedError
228 raise NotImplementedError
204
229
205 def transform(self, lines: List[str]):
230 def transform(self, lines: List[str]):
206 """Transform one instance of special syntax found by ``find()``
231 """Transform one instance of special syntax found by ``find()``
207
232
208 Takes a list of strings representing physical lines,
233 Takes a list of strings representing physical lines,
209 returns a similar list of transformed lines.
234 returns a similar list of transformed lines.
210 """
235 """
211 raise NotImplementedError
236 raise NotImplementedError
212
237
213 class MagicAssign(TokenTransformBase):
238 class MagicAssign(TokenTransformBase):
214 """Transformer for assignments from magics (a = %foo)"""
239 """Transformer for assignments from magics (a = %foo)"""
215 @classmethod
240 @classmethod
216 def find(cls, tokens_by_line):
241 def find(cls, tokens_by_line):
217 """Find the first magic assignment (a = %foo) in the cell.
242 """Find the first magic assignment (a = %foo) in the cell.
218 """
243 """
219 for line in tokens_by_line:
244 for line in tokens_by_line:
220 assign_ix = _find_assign_op(line)
245 assign_ix = _find_assign_op(line)
221 if (assign_ix is not None) \
246 if (assign_ix is not None) \
222 and (len(line) >= assign_ix + 2) \
247 and (len(line) >= assign_ix + 2) \
223 and (line[assign_ix+1].string == '%') \
248 and (line[assign_ix+1].string == '%') \
224 and (line[assign_ix+2].type == tokenize.NAME):
249 and (line[assign_ix+2].type == tokenize.NAME):
225 return cls(line[assign_ix+1].start)
250 return cls(line[assign_ix+1].start)
226
251
227 def transform(self, lines: List[str]):
252 def transform(self, lines: List[str]):
228 """Transform a magic assignment found by the ``find()`` classmethod.
253 """Transform a magic assignment found by the ``find()`` classmethod.
229 """
254 """
230 start_line, start_col = self.start_line, self.start_col
255 start_line, start_col = self.start_line, self.start_col
231 lhs = lines[start_line][:start_col]
256 lhs = lines[start_line][:start_col]
232 end_line = find_end_of_continued_line(lines, start_line)
257 end_line = find_end_of_continued_line(lines, start_line)
233 rhs = assemble_continued_line(lines, (start_line, start_col), end_line)
258 rhs = assemble_continued_line(lines, (start_line, start_col), end_line)
234 assert rhs.startswith('%'), rhs
259 assert rhs.startswith('%'), rhs
235 magic_name, _, args = rhs[1:].partition(' ')
260 magic_name, _, args = rhs[1:].partition(' ')
236
261
237 lines_before = lines[:start_line]
262 lines_before = lines[:start_line]
238 call = "get_ipython().run_line_magic({!r}, {!r})".format(magic_name, args)
263 call = "get_ipython().run_line_magic({!r}, {!r})".format(magic_name, args)
239 new_line = lhs + call + '\n'
264 new_line = lhs + call + '\n'
240 lines_after = lines[end_line+1:]
265 lines_after = lines[end_line+1:]
241
266
242 return lines_before + [new_line] + lines_after
267 return lines_before + [new_line] + lines_after
243
268
244
269
245 class SystemAssign(TokenTransformBase):
270 class SystemAssign(TokenTransformBase):
246 """Transformer for assignments from system commands (a = !foo)"""
271 """Transformer for assignments from system commands (a = !foo)"""
247 @classmethod
272 @classmethod
248 def find(cls, tokens_by_line):
273 def find(cls, tokens_by_line):
249 """Find the first system assignment (a = !foo) in the cell.
274 """Find the first system assignment (a = !foo) in the cell.
250 """
275 """
251 for line in tokens_by_line:
276 for line in tokens_by_line:
252 assign_ix = _find_assign_op(line)
277 assign_ix = _find_assign_op(line)
253 if (assign_ix is not None) \
278 if (assign_ix is not None) \
254 and not line[assign_ix].line.strip().startswith('=') \
279 and not line[assign_ix].line.strip().startswith('=') \
255 and (len(line) >= assign_ix + 2) \
280 and (len(line) >= assign_ix + 2) \
256 and (line[assign_ix + 1].type == tokenize.ERRORTOKEN):
281 and (line[assign_ix + 1].type == tokenize.ERRORTOKEN):
257 ix = assign_ix + 1
282 ix = assign_ix + 1
258
283
259 while ix < len(line) and line[ix].type == tokenize.ERRORTOKEN:
284 while ix < len(line) and line[ix].type == tokenize.ERRORTOKEN:
260 if line[ix].string == '!':
285 if line[ix].string == '!':
261 return cls(line[ix].start)
286 return cls(line[ix].start)
262 elif not line[ix].string.isspace():
287 elif not line[ix].string.isspace():
263 break
288 break
264 ix += 1
289 ix += 1
265
290
266 def transform(self, lines: List[str]):
291 def transform(self, lines: List[str]):
267 """Transform a system assignment found by the ``find()`` classmethod.
292 """Transform a system assignment found by the ``find()`` classmethod.
268 """
293 """
269 start_line, start_col = self.start_line, self.start_col
294 start_line, start_col = self.start_line, self.start_col
270
295
271 lhs = lines[start_line][:start_col]
296 lhs = lines[start_line][:start_col]
272 end_line = find_end_of_continued_line(lines, start_line)
297 end_line = find_end_of_continued_line(lines, start_line)
273 rhs = assemble_continued_line(lines, (start_line, start_col), end_line)
298 rhs = assemble_continued_line(lines, (start_line, start_col), end_line)
274 assert rhs.startswith('!'), rhs
299 assert rhs.startswith('!'), rhs
275 cmd = rhs[1:]
300 cmd = rhs[1:]
276
301
277 lines_before = lines[:start_line]
302 lines_before = lines[:start_line]
278 call = "get_ipython().getoutput({!r})".format(cmd)
303 call = "get_ipython().getoutput({!r})".format(cmd)
279 new_line = lhs + call + '\n'
304 new_line = lhs + call + '\n'
280 lines_after = lines[end_line + 1:]
305 lines_after = lines[end_line + 1:]
281
306
282 return lines_before + [new_line] + lines_after
307 return lines_before + [new_line] + lines_after
283
308
284 # The escape sequences that define the syntax transformations IPython will
309 # The escape sequences that define the syntax transformations IPython will
285 # apply to user input. These can NOT be just changed here: many regular
310 # apply to user input. These can NOT be just changed here: many regular
286 # expressions and other parts of the code may use their hardcoded values, and
311 # expressions and other parts of the code may use their hardcoded values, and
287 # for all intents and purposes they constitute the 'IPython syntax', so they
312 # for all intents and purposes they constitute the 'IPython syntax', so they
288 # should be considered fixed.
313 # should be considered fixed.
289
314
290 ESC_SHELL = '!' # Send line to underlying system shell
315 ESC_SHELL = '!' # Send line to underlying system shell
291 ESC_SH_CAP = '!!' # Send line to system shell and capture output
316 ESC_SH_CAP = '!!' # Send line to system shell and capture output
292 ESC_HELP = '?' # Find information about object
317 ESC_HELP = '?' # Find information about object
293 ESC_HELP2 = '??' # Find extra-detailed information about object
318 ESC_HELP2 = '??' # Find extra-detailed information about object
294 ESC_MAGIC = '%' # Call magic function
319 ESC_MAGIC = '%' # Call magic function
295 ESC_MAGIC2 = '%%' # Call cell-magic function
320 ESC_MAGIC2 = '%%' # Call cell-magic function
296 ESC_QUOTE = ',' # Split args on whitespace, quote each as string and call
321 ESC_QUOTE = ',' # Split args on whitespace, quote each as string and call
297 ESC_QUOTE2 = ';' # Quote all args as a single string, call
322 ESC_QUOTE2 = ';' # Quote all args as a single string, call
298 ESC_PAREN = '/' # Call first argument with rest of line as arguments
323 ESC_PAREN = '/' # Call first argument with rest of line as arguments
299
324
300 ESCAPE_SINGLES = {'!', '?', '%', ',', ';', '/'}
325 ESCAPE_SINGLES = {'!', '?', '%', ',', ';', '/'}
301 ESCAPE_DOUBLES = {'!!', '??'} # %% (cell magic) is handled separately
326 ESCAPE_DOUBLES = {'!!', '??'} # %% (cell magic) is handled separately
302
327
303 def _make_help_call(target, esc, next_input=None):
328 def _make_help_call(target, esc, next_input=None):
304 """Prepares a pinfo(2)/psearch call from a target name and the escape
329 """Prepares a pinfo(2)/psearch call from a target name and the escape
305 (i.e. ? or ??)"""
330 (i.e. ? or ??)"""
306 method = 'pinfo2' if esc == '??' \
331 method = 'pinfo2' if esc == '??' \
307 else 'psearch' if '*' in target \
332 else 'psearch' if '*' in target \
308 else 'pinfo'
333 else 'pinfo'
309 arg = " ".join([method, target])
334 arg = " ".join([method, target])
310 #Prepare arguments for get_ipython().run_line_magic(magic_name, magic_args)
335 #Prepare arguments for get_ipython().run_line_magic(magic_name, magic_args)
311 t_magic_name, _, t_magic_arg_s = arg.partition(' ')
336 t_magic_name, _, t_magic_arg_s = arg.partition(' ')
312 t_magic_name = t_magic_name.lstrip(ESC_MAGIC)
337 t_magic_name = t_magic_name.lstrip(ESC_MAGIC)
313 if next_input is None:
338 if next_input is None:
314 return 'get_ipython().run_line_magic(%r, %r)' % (t_magic_name, t_magic_arg_s)
339 return 'get_ipython().run_line_magic(%r, %r)' % (t_magic_name, t_magic_arg_s)
315 else:
340 else:
316 return 'get_ipython().set_next_input(%r);get_ipython().run_line_magic(%r, %r)' % \
341 return 'get_ipython().set_next_input(%r);get_ipython().run_line_magic(%r, %r)' % \
317 (next_input, t_magic_name, t_magic_arg_s)
342 (next_input, t_magic_name, t_magic_arg_s)
318
343
319 def _tr_help(content):
344 def _tr_help(content):
320 """Translate lines escaped with: ?
345 """Translate lines escaped with: ?
321
346
322 A naked help line should fire the intro help screen (shell.show_usage())
347 A naked help line should fire the intro help screen (shell.show_usage())
323 """
348 """
324 if not content:
349 if not content:
325 return 'get_ipython().show_usage()'
350 return 'get_ipython().show_usage()'
326
351
327 return _make_help_call(content, '?')
352 return _make_help_call(content, '?')
328
353
329 def _tr_help2(content):
354 def _tr_help2(content):
330 """Translate lines escaped with: ??
355 """Translate lines escaped with: ??
331
356
332 A naked help line should fire the intro help screen (shell.show_usage())
357 A naked help line should fire the intro help screen (shell.show_usage())
333 """
358 """
334 if not content:
359 if not content:
335 return 'get_ipython().show_usage()'
360 return 'get_ipython().show_usage()'
336
361
337 return _make_help_call(content, '??')
362 return _make_help_call(content, '??')
338
363
339 def _tr_magic(content):
364 def _tr_magic(content):
340 "Translate lines escaped with a percent sign: %"
365 "Translate lines escaped with a percent sign: %"
341 name, _, args = content.partition(' ')
366 name, _, args = content.partition(' ')
342 return 'get_ipython().run_line_magic(%r, %r)' % (name, args)
367 return 'get_ipython().run_line_magic(%r, %r)' % (name, args)
343
368
344 def _tr_quote(content):
369 def _tr_quote(content):
345 "Translate lines escaped with a comma: ,"
370 "Translate lines escaped with a comma: ,"
346 name, _, args = content.partition(' ')
371 name, _, args = content.partition(' ')
347 return '%s("%s")' % (name, '", "'.join(args.split()) )
372 return '%s("%s")' % (name, '", "'.join(args.split()) )
348
373
349 def _tr_quote2(content):
374 def _tr_quote2(content):
350 "Translate lines escaped with a semicolon: ;"
375 "Translate lines escaped with a semicolon: ;"
351 name, _, args = content.partition(' ')
376 name, _, args = content.partition(' ')
352 return '%s("%s")' % (name, args)
377 return '%s("%s")' % (name, args)
353
378
354 def _tr_paren(content):
379 def _tr_paren(content):
355 "Translate lines escaped with a slash: /"
380 "Translate lines escaped with a slash: /"
356 name, _, args = content.partition(' ')
381 name, _, args = content.partition(' ')
357 return '%s(%s)' % (name, ", ".join(args.split()))
382 return '%s(%s)' % (name, ", ".join(args.split()))
358
383
359 tr = { ESC_SHELL : 'get_ipython().system({!r})'.format,
384 tr = { ESC_SHELL : 'get_ipython().system({!r})'.format,
360 ESC_SH_CAP : 'get_ipython().getoutput({!r})'.format,
385 ESC_SH_CAP : 'get_ipython().getoutput({!r})'.format,
361 ESC_HELP : _tr_help,
386 ESC_HELP : _tr_help,
362 ESC_HELP2 : _tr_help2,
387 ESC_HELP2 : _tr_help2,
363 ESC_MAGIC : _tr_magic,
388 ESC_MAGIC : _tr_magic,
364 ESC_QUOTE : _tr_quote,
389 ESC_QUOTE : _tr_quote,
365 ESC_QUOTE2 : _tr_quote2,
390 ESC_QUOTE2 : _tr_quote2,
366 ESC_PAREN : _tr_paren }
391 ESC_PAREN : _tr_paren }
367
392
368 class EscapedCommand(TokenTransformBase):
393 class EscapedCommand(TokenTransformBase):
369 """Transformer for escaped commands like %foo, !foo, or /foo"""
394 """Transformer for escaped commands like %foo, !foo, or /foo"""
370 @classmethod
395 @classmethod
371 def find(cls, tokens_by_line):
396 def find(cls, tokens_by_line):
372 """Find the first escaped command (%foo, !foo, etc.) in the cell.
397 """Find the first escaped command (%foo, !foo, etc.) in the cell.
373 """
398 """
374 for line in tokens_by_line:
399 for line in tokens_by_line:
375 if not line:
400 if not line:
376 continue
401 continue
377 ix = 0
402 ix = 0
378 ll = len(line)
403 ll = len(line)
379 while ll > ix and line[ix].type in {tokenize.INDENT, tokenize.DEDENT}:
404 while ll > ix and line[ix].type in {tokenize.INDENT, tokenize.DEDENT}:
380 ix += 1
405 ix += 1
381 if ix >= ll:
406 if ix >= ll:
382 continue
407 continue
383 if line[ix].string in ESCAPE_SINGLES:
408 if line[ix].string in ESCAPE_SINGLES:
384 return cls(line[ix].start)
409 return cls(line[ix].start)
385
410
386 def transform(self, lines):
411 def transform(self, lines):
387 """Transform an escaped line found by the ``find()`` classmethod.
412 """Transform an escaped line found by the ``find()`` classmethod.
388 """
413 """
389 start_line, start_col = self.start_line, self.start_col
414 start_line, start_col = self.start_line, self.start_col
390
415
391 indent = lines[start_line][:start_col]
416 indent = lines[start_line][:start_col]
392 end_line = find_end_of_continued_line(lines, start_line)
417 end_line = find_end_of_continued_line(lines, start_line)
393 line = assemble_continued_line(lines, (start_line, start_col), end_line)
418 line = assemble_continued_line(lines, (start_line, start_col), end_line)
394
419
395 if len(line) > 1 and line[:2] in ESCAPE_DOUBLES:
420 if len(line) > 1 and line[:2] in ESCAPE_DOUBLES:
396 escape, content = line[:2], line[2:]
421 escape, content = line[:2], line[2:]
397 else:
422 else:
398 escape, content = line[:1], line[1:]
423 escape, content = line[:1], line[1:]
399
424
400 if escape in tr:
425 if escape in tr:
401 call = tr[escape](content)
426 call = tr[escape](content)
402 else:
427 else:
403 call = ''
428 call = ''
404
429
405 lines_before = lines[:start_line]
430 lines_before = lines[:start_line]
406 new_line = indent + call + '\n'
431 new_line = indent + call + '\n'
407 lines_after = lines[end_line + 1:]
432 lines_after = lines[end_line + 1:]
408
433
409 return lines_before + [new_line] + lines_after
434 return lines_before + [new_line] + lines_after
410
435
411 _help_end_re = re.compile(r"""(%{0,2}
436 _help_end_re = re.compile(r"""(%{0,2}
412 (?!\d)[\w*]+ # Variable name
437 (?!\d)[\w*]+ # Variable name
413 (\.(?!\d)[\w*]+)* # .etc.etc
438 (\.(?!\d)[\w*]+)* # .etc.etc
414 )
439 )
415 (\?\??)$ # ? or ??
440 (\?\??)$ # ? or ??
416 """,
441 """,
417 re.VERBOSE)
442 re.VERBOSE)
418
443
419 class HelpEnd(TokenTransformBase):
444 class HelpEnd(TokenTransformBase):
420 """Transformer for help syntax: obj? and obj??"""
445 """Transformer for help syntax: obj? and obj??"""
421 # This needs to be higher priority (lower number) than EscapedCommand so
446 # This needs to be higher priority (lower number) than EscapedCommand so
422 # that inspecting magics (%foo?) works.
447 # that inspecting magics (%foo?) works.
423 priority = 5
448 priority = 5
424
449
425 def __init__(self, start, q_locn):
450 def __init__(self, start, q_locn):
426 super().__init__(start)
451 super().__init__(start)
427 self.q_line = q_locn[0] - 1 # Shift from 1-indexed to 0-indexed
452 self.q_line = q_locn[0] - 1 # Shift from 1-indexed to 0-indexed
428 self.q_col = q_locn[1]
453 self.q_col = q_locn[1]
429
454
430 @classmethod
455 @classmethod
431 def find(cls, tokens_by_line):
456 def find(cls, tokens_by_line):
432 """Find the first help command (foo?) in the cell.
457 """Find the first help command (foo?) in the cell.
433 """
458 """
434 for line in tokens_by_line:
459 for line in tokens_by_line:
435 # Last token is NEWLINE; look at last but one
460 # Last token is NEWLINE; look at last but one
436 if len(line) > 2 and line[-2].string == '?':
461 if len(line) > 2 and line[-2].string == '?':
437 # Find the first token that's not INDENT/DEDENT
462 # Find the first token that's not INDENT/DEDENT
438 ix = 0
463 ix = 0
439 while line[ix].type in {tokenize.INDENT, tokenize.DEDENT}:
464 while line[ix].type in {tokenize.INDENT, tokenize.DEDENT}:
440 ix += 1
465 ix += 1
441 return cls(line[ix].start, line[-2].start)
466 return cls(line[ix].start, line[-2].start)
442
467
443 def transform(self, lines):
468 def transform(self, lines):
444 """Transform a help command found by the ``find()`` classmethod.
469 """Transform a help command found by the ``find()`` classmethod.
445 """
470 """
446 piece = ''.join(lines[self.start_line:self.q_line+1])
471 piece = ''.join(lines[self.start_line:self.q_line+1])
447 indent, content = piece[:self.start_col], piece[self.start_col:]
472 indent, content = piece[:self.start_col], piece[self.start_col:]
448 lines_before = lines[:self.start_line]
473 lines_before = lines[:self.start_line]
449 lines_after = lines[self.q_line + 1:]
474 lines_after = lines[self.q_line + 1:]
450
475
451 m = _help_end_re.search(content)
476 m = _help_end_re.search(content)
452 if not m:
477 if not m:
453 raise SyntaxError(content)
478 raise SyntaxError(content)
454 assert m is not None, content
479 assert m is not None, content
455 target = m.group(1)
480 target = m.group(1)
456 esc = m.group(3)
481 esc = m.group(3)
457
482
458 # If we're mid-command, put it back on the next prompt for the user.
483 # If we're mid-command, put it back on the next prompt for the user.
459 next_input = None
484 next_input = None
460 if (not lines_before) and (not lines_after) \
485 if (not lines_before) and (not lines_after) \
461 and content.strip() != m.group(0):
486 and content.strip() != m.group(0):
462 next_input = content.rstrip('?\n')
487 next_input = content.rstrip('?\n')
463
488
464 call = _make_help_call(target, esc, next_input=next_input)
489 call = _make_help_call(target, esc, next_input=next_input)
465 new_line = indent + call + '\n'
490 new_line = indent + call + '\n'
466
491
467 return lines_before + [new_line] + lines_after
492 return lines_before + [new_line] + lines_after
468
493
469 def make_tokens_by_line(lines:List[str]):
494 def make_tokens_by_line(lines:List[str]):
470 """Tokenize a series of lines and group tokens by line.
495 """Tokenize a series of lines and group tokens by line.
471
496
472 The tokens for a multiline Python string or expression are grouped as one
497 The tokens for a multiline Python string or expression are grouped as one
473 line. All lines except the last lines should keep their line ending ('\\n',
498 line. All lines except the last lines should keep their line ending ('\\n',
474 '\\r\\n') for this to properly work. Use `.splitlines(keeplineending=True)`
499 '\\r\\n') for this to properly work. Use `.splitlines(keeplineending=True)`
475 for example when passing block of text to this function.
500 for example when passing block of text to this function.
476
501
477 """
502 """
478 # NL tokens are used inside multiline expressions, but also after blank
503 # NL tokens are used inside multiline expressions, but also after blank
479 # lines or comments. This is intentional - see https://bugs.python.org/issue17061
504 # lines or comments. This is intentional - see https://bugs.python.org/issue17061
480 # We want to group the former case together but split the latter, so we
505 # We want to group the former case together but split the latter, so we
481 # track parentheses level, similar to the internals of tokenize.
506 # track parentheses level, similar to the internals of tokenize.
482
507
483 # reexported from token on 3.7+
508 # reexported from token on 3.7+
484 NEWLINE, NL = tokenize.NEWLINE, tokenize.NL # type: ignore
509 NEWLINE, NL = tokenize.NEWLINE, tokenize.NL # type: ignore
485 tokens_by_line:List[List[Any]] = [[]]
510 tokens_by_line:List[List[Any]] = [[]]
486 if len(lines) > 1 and not lines[0].endswith(('\n', '\r', '\r\n', '\x0b', '\x0c')):
511 if len(lines) > 1 and not lines[0].endswith(('\n', '\r', '\r\n', '\x0b', '\x0c')):
487 warnings.warn("`make_tokens_by_line` received a list of lines which do not have lineending markers ('\\n', '\\r', '\\r\\n', '\\x0b', '\\x0c'), behavior will be unspecified")
512 warnings.warn("`make_tokens_by_line` received a list of lines which do not have lineending markers ('\\n', '\\r', '\\r\\n', '\\x0b', '\\x0c'), behavior will be unspecified")
488 parenlev = 0
513 parenlev = 0
489 try:
514 try:
490 for token in tokenize.generate_tokens(iter(lines).__next__):
515 for token in tokenize.generate_tokens(iter(lines).__next__):
491 tokens_by_line[-1].append(token)
516 tokens_by_line[-1].append(token)
492 if (token.type == NEWLINE) \
517 if (token.type == NEWLINE) \
493 or ((token.type == NL) and (parenlev <= 0)):
518 or ((token.type == NL) and (parenlev <= 0)):
494 tokens_by_line.append([])
519 tokens_by_line.append([])
495 elif token.string in {'(', '[', '{'}:
520 elif token.string in {'(', '[', '{'}:
496 parenlev += 1
521 parenlev += 1
497 elif token.string in {')', ']', '}'}:
522 elif token.string in {')', ']', '}'}:
498 if parenlev > 0:
523 if parenlev > 0:
499 parenlev -= 1
524 parenlev -= 1
500 except tokenize.TokenError:
525 except tokenize.TokenError:
501 # Input ended in a multiline string or expression. That's OK for us.
526 # Input ended in a multiline string or expression. That's OK for us.
502 pass
527 pass
503
528
504
529
505 if not tokens_by_line[-1]:
530 if not tokens_by_line[-1]:
506 tokens_by_line.pop()
531 tokens_by_line.pop()
507
532
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'):
514 s += '\n'
553 s += '\n'
515 lines = s.splitlines(keepends=True)
554 lines = s.splitlines(keepends=True)
516 for line in make_tokens_by_line(lines):
555 for line in make_tokens_by_line(lines):
517 print("Line -------")
556 print("Line -------")
518 for tokinfo in line:
557 for tokinfo in line:
519 print(" ", tokinfo)
558 print(" ", tokinfo)
520
559
521 # Arbitrary limit to prevent getting stuck in infinite loops
560 # Arbitrary limit to prevent getting stuck in infinite loops
522 TRANSFORM_LOOP_LIMIT = 500
561 TRANSFORM_LOOP_LIMIT = 500
523
562
524 class TransformerManager:
563 class TransformerManager:
525 """Applies various transformations to a cell or code block.
564 """Applies various transformations to a cell or code block.
526
565
527 The key methods for external use are ``transform_cell()``
566 The key methods for external use are ``transform_cell()``
528 and ``check_complete()``.
567 and ``check_complete()``.
529 """
568 """
530 def __init__(self):
569 def __init__(self):
531 self.cleanup_transforms = [
570 self.cleanup_transforms = [
532 leading_empty_lines,
571 leading_empty_lines,
533 leading_indent,
572 leading_indent,
534 classic_prompt,
573 classic_prompt,
535 ipython_prompt,
574 ipython_prompt,
536 ]
575 ]
537 self.line_transforms = [
576 self.line_transforms = [
538 cell_magic,
577 cell_magic,
539 ]
578 ]
540 self.token_transformers = [
579 self.token_transformers = [
541 MagicAssign,
580 MagicAssign,
542 SystemAssign,
581 SystemAssign,
543 EscapedCommand,
582 EscapedCommand,
544 HelpEnd,
583 HelpEnd,
545 ]
584 ]
546
585
547 def do_one_token_transform(self, lines):
586 def do_one_token_transform(self, lines):
548 """Find and run the transform earliest in the code.
587 """Find and run the transform earliest in the code.
549
588
550 Returns (changed, lines).
589 Returns (changed, lines).
551
590
552 This method is called repeatedly until changed is False, indicating
591 This method is called repeatedly until changed is False, indicating
553 that all available transformations are complete.
592 that all available transformations are complete.
554
593
555 The tokens following IPython special syntax might not be valid, so
594 The tokens following IPython special syntax might not be valid, so
556 the transformed code is retokenised every time to identify the next
595 the transformed code is retokenised every time to identify the next
557 piece of special syntax. Hopefully long code cells are mostly valid
596 piece of special syntax. Hopefully long code cells are mostly valid
558 Python, not using lots of IPython special syntax, so this shouldn't be
597 Python, not using lots of IPython special syntax, so this shouldn't be
559 a performance issue.
598 a performance issue.
560 """
599 """
561 tokens_by_line = make_tokens_by_line(lines)
600 tokens_by_line = make_tokens_by_line(lines)
562 candidates = []
601 candidates = []
563 for transformer_cls in self.token_transformers:
602 for transformer_cls in self.token_transformers:
564 transformer = transformer_cls.find(tokens_by_line)
603 transformer = transformer_cls.find(tokens_by_line)
565 if transformer:
604 if transformer:
566 candidates.append(transformer)
605 candidates.append(transformer)
567
606
568 if not candidates:
607 if not candidates:
569 # Nothing to transform
608 # Nothing to transform
570 return False, lines
609 return False, lines
571 ordered_transformers = sorted(candidates, key=TokenTransformBase.sortby)
610 ordered_transformers = sorted(candidates, key=TokenTransformBase.sortby)
572 for transformer in ordered_transformers:
611 for transformer in ordered_transformers:
573 try:
612 try:
574 return True, transformer.transform(lines)
613 return True, transformer.transform(lines)
575 except SyntaxError:
614 except SyntaxError:
576 pass
615 pass
577 return False, lines
616 return False, lines
578
617
579 def do_token_transforms(self, lines):
618 def do_token_transforms(self, lines):
580 for _ in range(TRANSFORM_LOOP_LIMIT):
619 for _ in range(TRANSFORM_LOOP_LIMIT):
581 changed, lines = self.do_one_token_transform(lines)
620 changed, lines = self.do_one_token_transform(lines)
582 if not changed:
621 if not changed:
583 return lines
622 return lines
584
623
585 raise RuntimeError("Input transformation still changing after "
624 raise RuntimeError("Input transformation still changing after "
586 "%d iterations. Aborting." % TRANSFORM_LOOP_LIMIT)
625 "%d iterations. Aborting." % TRANSFORM_LOOP_LIMIT)
587
626
588 def transform_cell(self, cell: str) -> str:
627 def transform_cell(self, cell: str) -> str:
589 """Transforms a cell of input code"""
628 """Transforms a cell of input code"""
590 if not cell.endswith('\n'):
629 if not cell.endswith('\n'):
591 cell += '\n' # Ensure the cell has a trailing newline
630 cell += '\n' # Ensure the cell has a trailing newline
592 lines = cell.splitlines(keepends=True)
631 lines = cell.splitlines(keepends=True)
593 for transform in self.cleanup_transforms + self.line_transforms:
632 for transform in self.cleanup_transforms + self.line_transforms:
594 lines = transform(lines)
633 lines = transform(lines)
595
634
596 lines = self.do_token_transforms(lines)
635 lines = self.do_token_transforms(lines)
597 return ''.join(lines)
636 return ''.join(lines)
598
637
599 def check_complete(self, cell: str):
638 def check_complete(self, cell: str):
600 """Return whether a block of code is ready to execute, or should be continued
639 """Return whether a block of code is ready to execute, or should be continued
601
640
602 Parameters
641 Parameters
603 ----------
642 ----------
604 source : string
643 source : string
605 Python input code, which can be multiline.
644 Python input code, which can be multiline.
606
645
607 Returns
646 Returns
608 -------
647 -------
609 status : str
648 status : str
610 One of 'complete', 'incomplete', or 'invalid' if source is not a
649 One of 'complete', 'incomplete', or 'invalid' if source is not a
611 prefix of valid code.
650 prefix of valid code.
612 indent_spaces : int or None
651 indent_spaces : int or None
613 The number of spaces by which to indent the next line of code. If
652 The number of spaces by which to indent the next line of code. If
614 status is not 'incomplete', this is None.
653 status is not 'incomplete', this is None.
615 """
654 """
616 # Remember if the lines ends in a new line.
655 # Remember if the lines ends in a new line.
617 ends_with_newline = False
656 ends_with_newline = False
618 for character in reversed(cell):
657 for character in reversed(cell):
619 if character == '\n':
658 if character == '\n':
620 ends_with_newline = True
659 ends_with_newline = True
621 break
660 break
622 elif character.strip():
661 elif character.strip():
623 break
662 break
624 else:
663 else:
625 continue
664 continue
626
665
627 if not ends_with_newline:
666 if not ends_with_newline:
628 # Append an newline for consistent tokenization
667 # Append an newline for consistent tokenization
629 # See https://bugs.python.org/issue33899
668 # See https://bugs.python.org/issue33899
630 cell += '\n'
669 cell += '\n'
631
670
632 lines = cell.splitlines(keepends=True)
671 lines = cell.splitlines(keepends=True)
633
672
634 if not lines:
673 if not lines:
635 return 'complete', None
674 return 'complete', None
636
675
637 if lines[-1].endswith('\\'):
676 if lines[-1].endswith('\\'):
638 # Explicit backslash continuation
677 # Explicit backslash continuation
639 return 'incomplete', find_last_indent(lines)
678 return 'incomplete', find_last_indent(lines)
640
679
641 try:
680 try:
642 for transform in self.cleanup_transforms:
681 for transform in self.cleanup_transforms:
643 if not getattr(transform, 'has_side_effects', False):
682 if not getattr(transform, 'has_side_effects', False):
644 lines = transform(lines)
683 lines = transform(lines)
645 except SyntaxError:
684 except SyntaxError:
646 return 'invalid', None
685 return 'invalid', None
647
686
648 if lines[0].startswith('%%'):
687 if lines[0].startswith('%%'):
649 # Special case for cell magics - completion marked by blank line
688 # Special case for cell magics - completion marked by blank line
650 if lines[-1].strip():
689 if lines[-1].strip():
651 return 'incomplete', find_last_indent(lines)
690 return 'incomplete', find_last_indent(lines)
652 else:
691 else:
653 return 'complete', None
692 return 'complete', None
654
693
655 try:
694 try:
656 for transform in self.line_transforms:
695 for transform in self.line_transforms:
657 if not getattr(transform, 'has_side_effects', False):
696 if not getattr(transform, 'has_side_effects', False):
658 lines = transform(lines)
697 lines = transform(lines)
659 lines = self.do_token_transforms(lines)
698 lines = self.do_token_transforms(lines)
660 except SyntaxError:
699 except SyntaxError:
661 return 'invalid', None
700 return 'invalid', None
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
668 if tokens_by_line[-1][-1].type != tokenize.ENDMARKER:
716 if tokens_by_line[-1][-1].type != tokenize.ENDMARKER:
669 # We're in a multiline string or expression
717 # We're in a multiline string or expression
670 return 'incomplete', find_last_indent(lines)
718 return 'incomplete', find_last_indent(lines)
671
719
672 newline_types = {tokenize.NEWLINE, tokenize.COMMENT, tokenize.ENDMARKER} # type: ignore
720 newline_types = {tokenize.NEWLINE, tokenize.COMMENT, tokenize.ENDMARKER} # type: ignore
673
721
674 # Pop the last line which only contains DEDENTs and ENDMARKER
722 # Pop the last line which only contains DEDENTs and ENDMARKER
675 last_token_line = None
723 last_token_line = None
676 if {t.type for t in tokens_by_line[-1]} in [
724 if {t.type for t in tokens_by_line[-1]} in [
677 {tokenize.DEDENT, tokenize.ENDMARKER},
725 {tokenize.DEDENT, tokenize.ENDMARKER},
678 {tokenize.ENDMARKER}
726 {tokenize.ENDMARKER}
679 ] and len(tokens_by_line) > 1:
727 ] and len(tokens_by_line) > 1:
680 last_token_line = tokens_by_line.pop()
728 last_token_line = tokens_by_line.pop()
681
729
682 while tokens_by_line[-1] and tokens_by_line[-1][-1].type in newline_types:
730 while tokens_by_line[-1] and tokens_by_line[-1][-1].type in newline_types:
683 tokens_by_line[-1].pop()
731 tokens_by_line[-1].pop()
684
732
685 if not tokens_by_line[-1]:
733 if not tokens_by_line[-1]:
686 return 'incomplete', find_last_indent(lines)
734 return 'incomplete', find_last_indent(lines)
687
735
688 if tokens_by_line[-1][-1].string == ':':
736 if tokens_by_line[-1][-1].string == ':':
689 # The last line starts a block (e.g. 'if foo:')
737 # The last line starts a block (e.g. 'if foo:')
690 ix = 0
738 ix = 0
691 while tokens_by_line[-1][ix].type in {tokenize.INDENT, tokenize.DEDENT}:
739 while tokens_by_line[-1][ix].type in {tokenize.INDENT, tokenize.DEDENT}:
692 ix += 1
740 ix += 1
693
741
694 indent = tokens_by_line[-1][ix].start[1]
742 indent = tokens_by_line[-1][ix].start[1]
695 return 'incomplete', indent + 4
743 return 'incomplete', indent + 4
696
744
697 if tokens_by_line[-1][0].line.endswith('\\'):
745 if tokens_by_line[-1][0].line.endswith('\\'):
698 return 'incomplete', None
746 return 'incomplete', None
699
747
700 # At this point, our checks think the code is complete (or invalid).
748 # At this point, our checks think the code is complete (or invalid).
701 # We'll use codeop.compile_command to check this with the real parser
749 # We'll use codeop.compile_command to check this with the real parser
702 try:
750 try:
703 with warnings.catch_warnings():
751 with warnings.catch_warnings():
704 warnings.simplefilter('error', SyntaxWarning)
752 warnings.simplefilter('error', SyntaxWarning)
705 res = compile_command(''.join(lines), symbol='exec')
753 res = compile_command(''.join(lines), symbol='exec')
706 except (SyntaxError, OverflowError, ValueError, TypeError,
754 except (SyntaxError, OverflowError, ValueError, TypeError,
707 MemoryError, SyntaxWarning):
755 MemoryError, SyntaxWarning):
708 return 'invalid', None
756 return 'invalid', None
709 else:
757 else:
710 if res is None:
758 if res is None:
711 return 'incomplete', find_last_indent(lines)
759 return 'incomplete', find_last_indent(lines)
712
760
713 if last_token_line and last_token_line[0].type == tokenize.DEDENT:
761 if last_token_line and last_token_line[0].type == tokenize.DEDENT:
714 if ends_with_newline:
762 if ends_with_newline:
715 return 'complete', None
763 return 'complete', None
716 return 'incomplete', find_last_indent(lines)
764 return 'incomplete', find_last_indent(lines)
717
765
718 # If there's a blank line at the end, assume we're ready to execute
766 # If there's a blank line at the end, assume we're ready to execute
719 if not lines[-1].strip():
767 if not lines[-1].strip():
720 return 'complete', None
768 return 'complete', None
721
769
722 return 'complete', None
770 return 'complete', None
723
771
724
772
725 def find_last_indent(lines):
773 def find_last_indent(lines):
726 m = _indent_re.match(lines[-1])
774 m = _indent_re.match(lines[-1])
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)
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
1 NO CONTENT: file was removed
NO CONTENT: file was removed
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now