euporie 2.8.6__tar.gz → 2.8.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {euporie-2.8.6 → euporie-2.8.7}/.gitignore +1 -142
- {euporie-2.8.6 → euporie-2.8.7}/LICENSE +1 -1
- {euporie-2.8.6 → euporie-2.8.7}/PKG-INFO +1 -1
- {euporie-2.8.6 → euporie-2.8.7}/euporie/console/app.py +2 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/console/tabs/console.py +27 -17
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/__init__.py +2 -2
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/app/_commands.py +4 -21
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/app/app.py +13 -7
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/bars/command.py +9 -6
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/bars/search.py +43 -2
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/border.py +7 -2
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/comm/base.py +2 -2
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/comm/ipywidgets.py +3 -3
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/commands.py +44 -8
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/completion.py +14 -6
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/convert/datum.py +7 -7
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/data_structures.py +20 -1
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/filters.py +8 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/ft/html.py +47 -40
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/graphics.py +11 -3
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/history.py +15 -5
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/inspection.py +16 -9
- euporie-2.8.7/euporie/core/kernel/__init__.py +53 -0
- euporie-2.8.7/euporie/core/kernel/base.py +571 -0
- euporie-2.8.6/euporie/core/kernel/client.py → euporie-2.8.7/euporie/core/kernel/jupyter.py +173 -430
- euporie-2.8.6/euporie/core/kernel/manager.py → euporie-2.8.7/euporie/core/kernel/jupyter_manager.py +4 -3
- euporie-2.8.7/euporie/core/kernel/local.py +694 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/key_binding/bindings/basic.py +6 -3
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/keys.py +26 -25
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/layout/cache.py +31 -7
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/layout/containers.py +88 -13
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/layout/scroll.py +45 -148
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/log.py +1 -1
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/style.py +2 -1
- euporie-2.8.7/euporie/core/suggest.py +241 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/tabs/__init__.py +10 -0
- euporie-2.8.7/euporie/core/tabs/_commands.py +76 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/tabs/_settings.py +16 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/tabs/base.py +22 -8
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/tabs/kernel.py +81 -35
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/tabs/notebook.py +14 -22
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/utils.py +1 -1
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/validation.py +8 -8
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/widgets/_settings.py +19 -2
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/widgets/cell.py +31 -31
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/widgets/cell_outputs.py +10 -1
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/widgets/dialog.py +30 -75
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/widgets/forms.py +71 -59
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/widgets/inputs.py +7 -4
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/widgets/layout.py +281 -93
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/widgets/menu.py +55 -15
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/widgets/palette.py +3 -1
- euporie-2.8.7/euporie/core/widgets/tree.py +119 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/notebook/app.py +35 -16
- {euporie-2.8.6 → euporie-2.8.7}/euporie/notebook/tabs/edit.py +4 -4
- {euporie-2.8.6 → euporie-2.8.7}/euporie/notebook/tabs/json.py +6 -2
- {euporie-2.8.6 → euporie-2.8.7}/euporie/notebook/tabs/notebook.py +26 -8
- {euporie-2.8.6 → euporie-2.8.7}/euporie/preview/tabs/notebook.py +17 -13
- {euporie-2.8.6 → euporie-2.8.7}/euporie/web/tabs/web.py +22 -3
- {euporie-2.8.6 → euporie-2.8.7}/euporie/web/widgets/webview.py +3 -0
- {euporie-2.8.6 → euporie-2.8.7}/pyproject.toml +1 -1
- euporie-2.8.6/euporie/core/kernel/__init__.py +0 -1
- euporie-2.8.6/euporie/core/suggest.py +0 -160
- euporie-2.8.6/euporie/core/widgets/tree.py +0 -109
- {euporie-2.8.6 → euporie-2.8.7}/README.rst +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/console/__init__.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/console/__main__.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/console/_commands.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/console/_settings.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/console/py.typed +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/console/tabs/__init__.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/__main__.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/_settings.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/app/__init__.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/app/_settings.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/app/base.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/app/current.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/app/cursor.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/app/dummy.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/app/launch.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/bars/__init__.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/bars/menu.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/bars/status.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/clipboard.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/comm/__init__.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/comm/registry.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/config.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/convert/__init__.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/convert/formats/__init__.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/convert/formats/ansi.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/convert/formats/base64.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/convert/formats/common.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/convert/formats/ft.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/convert/formats/html.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/convert/formats/jpeg.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/convert/formats/markdown.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/convert/formats/pdf.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/convert/formats/pil.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/convert/formats/png.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/convert/formats/rich.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/convert/formats/sixel.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/convert/formats/svg.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/convert/mime.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/convert/registry.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/convert/utils.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/diagnostics.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/format.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/ft/__init__.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/ft/ansi.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/ft/table.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/ft/utils.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/io.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/key_binding/__init__.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/key_binding/bindings/__init__.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/key_binding/bindings/completion.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/key_binding/bindings/micro.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/key_binding/bindings/mouse.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/key_binding/bindings/page_navigation.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/key_binding/bindings/terminal.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/key_binding/bindings/vi.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/key_binding/key_processor.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/key_binding/micro_state.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/key_binding/registry.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/key_binding/utils.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/key_binding/vi_state.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/layout/__init__.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/layout/controls.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/layout/decor.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/layout/mouse.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/layout/print.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/layout/screen.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/lexers.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/lsp.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/margins.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/path.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/processors.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/py.typed +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/pygments.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/reference.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/renderer.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/widgets/__init__.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/widgets/decor.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/widgets/display.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/widgets/file_browser.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/widgets/formatted_text_area.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/widgets/logo.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/core/widgets/pager.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/data/desktop/euporie-console.desktop +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/data/desktop/euporie-notebook.desktop +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/hub/__init__.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/hub/__main__.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/hub/app.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/hub/py.typed +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/notebook/__init__.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/notebook/__main__.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/notebook/_commands.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/notebook/_settings.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/notebook/current.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/notebook/enums.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/notebook/filters.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/notebook/py.typed +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/notebook/tabs/__init__.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/notebook/tabs/_commands.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/notebook/tabs/_settings.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/notebook/tabs/display.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/notebook/tabs/log.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/notebook/widgets/__init__.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/notebook/widgets/_commands.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/notebook/widgets/_settings.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/notebook/widgets/side_bar.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/preview/__init__.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/preview/__main__.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/preview/_settings.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/preview/app.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/preview/py.typed +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/preview/tabs/__init__.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/web/__init__.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/web/tabs/__init__.py +0 -0
- {euporie-2.8.6 → euporie-2.8.7}/euporie/web/widgets/__init__.py +0 -0
@@ -2,9 +2,6 @@
|
|
2
2
|
/media
|
3
3
|
/examples
|
4
4
|
|
5
|
-
# Python version specifier file
|
6
|
-
.python-version
|
7
|
-
|
8
5
|
# AI tools
|
9
6
|
.aider*
|
10
7
|
|
@@ -97,7 +94,7 @@ ipython_config.py
|
|
97
94
|
# pyenv
|
98
95
|
# For a library or package, you might want to ignore these files since the code is
|
99
96
|
# intended to run in multiple environments; otherwise, check them in:
|
100
|
-
|
97
|
+
.python-version
|
101
98
|
|
102
99
|
# pipenv
|
103
100
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
@@ -151,141 +148,3 @@ dmypy.json
|
|
151
148
|
|
152
149
|
# Cython debug symbols
|
153
150
|
cython_debug/
|
154
|
-
# Byte-compiled / optimized / DLL files
|
155
|
-
__pycache__/
|
156
|
-
*.py[cod]
|
157
|
-
*$py.class
|
158
|
-
|
159
|
-
# C extensions
|
160
|
-
*.so
|
161
|
-
|
162
|
-
# Distribution / packaging
|
163
|
-
.Python
|
164
|
-
build/
|
165
|
-
develop-eggs/
|
166
|
-
dist/
|
167
|
-
downloads/
|
168
|
-
eggs/
|
169
|
-
.eggs/
|
170
|
-
lib/
|
171
|
-
lib64/
|
172
|
-
parts/
|
173
|
-
sdist/
|
174
|
-
var/
|
175
|
-
wheels/
|
176
|
-
share/python-wheels/
|
177
|
-
*.egg-info/
|
178
|
-
.installed.cfg
|
179
|
-
*.egg
|
180
|
-
MANIFEST
|
181
|
-
|
182
|
-
# PyInstaller
|
183
|
-
# Usually these files are written by a python script from a template
|
184
|
-
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
185
|
-
*.manifest
|
186
|
-
*.spec
|
187
|
-
|
188
|
-
# Installer logs
|
189
|
-
pip-log.txt
|
190
|
-
pip-delete-this-directory.txt
|
191
|
-
|
192
|
-
# Unit test / coverage reports
|
193
|
-
htmlcov/
|
194
|
-
.tox/
|
195
|
-
.nox/
|
196
|
-
.coverage
|
197
|
-
.coverage.*
|
198
|
-
.cache
|
199
|
-
nosetests.xml
|
200
|
-
coverage.xml
|
201
|
-
*.cover
|
202
|
-
*.py,cover
|
203
|
-
.hypothesis/
|
204
|
-
.pytest_cache/
|
205
|
-
cover/
|
206
|
-
|
207
|
-
# Translations
|
208
|
-
*.mo
|
209
|
-
*.pot
|
210
|
-
|
211
|
-
# Django stuff:
|
212
|
-
*.log
|
213
|
-
local_settings.py
|
214
|
-
db.sqlite3
|
215
|
-
db.sqlite3-journal
|
216
|
-
|
217
|
-
# Flask stuff:
|
218
|
-
instance/
|
219
|
-
.webassets-cache
|
220
|
-
|
221
|
-
# Scrapy stuff:
|
222
|
-
.scrapy
|
223
|
-
|
224
|
-
# Sphinx documentation
|
225
|
-
docs/_build/
|
226
|
-
|
227
|
-
# PyBuilder
|
228
|
-
.pybuilder/
|
229
|
-
target/
|
230
|
-
|
231
|
-
# Jupyter Notebook
|
232
|
-
.ipynb_checkpoints
|
233
|
-
|
234
|
-
# IPython
|
235
|
-
profile_default/
|
236
|
-
ipython_config.py
|
237
|
-
|
238
|
-
# pyenv
|
239
|
-
# For a library or package, you might want to ignore these files since the code is
|
240
|
-
# intended to run in multiple environments; otherwise, check them in:
|
241
|
-
# .python-version
|
242
|
-
|
243
|
-
# pipenv
|
244
|
-
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
245
|
-
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
246
|
-
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
247
|
-
# install all needed dependencies.
|
248
|
-
#Pipfile.lock
|
249
|
-
|
250
|
-
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
251
|
-
__pypackages__/
|
252
|
-
|
253
|
-
# Celery stuff
|
254
|
-
celerybeat-schedule
|
255
|
-
celerybeat.pid
|
256
|
-
|
257
|
-
# SageMath parsed files
|
258
|
-
*.sage.py
|
259
|
-
|
260
|
-
# Environments
|
261
|
-
.env
|
262
|
-
.venv
|
263
|
-
env/
|
264
|
-
venv/
|
265
|
-
ENV/
|
266
|
-
env.bak/
|
267
|
-
venv.bak/
|
268
|
-
|
269
|
-
# Spyder project settings
|
270
|
-
.spyderproject
|
271
|
-
.spyproject
|
272
|
-
|
273
|
-
# Rope project settings
|
274
|
-
.ropeproject
|
275
|
-
|
276
|
-
# mkdocs documentation
|
277
|
-
/site
|
278
|
-
|
279
|
-
# mypy
|
280
|
-
.mypy_cache/
|
281
|
-
.dmypy.json
|
282
|
-
dmypy.json
|
283
|
-
|
284
|
-
# Pyre type checker
|
285
|
-
.pyre/
|
286
|
-
|
287
|
-
# pytype static type analyzer
|
288
|
-
.pytype/
|
289
|
-
|
290
|
-
# Cython debug symbols
|
291
|
-
cython_debug/
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: euporie
|
3
|
-
Version: 2.8.
|
3
|
+
Version: 2.8.7
|
4
4
|
Summary: Euporie is a suite of terminal applications for interacting with Jupyter kernels
|
5
5
|
Project-URL: Documentation, https://euporie.readthedocs.io/en/latest
|
6
6
|
Project-URL: Issues, https://github.com/joouha/euporie/issues
|
@@ -32,6 +32,7 @@ from euporie.core.filters import has_dialog
|
|
32
32
|
from euporie.core.layout.mouse import DisableMouseOnScroll
|
33
33
|
from euporie.core.widgets.dialog import (
|
34
34
|
AboutDialog,
|
35
|
+
ConfirmDialog,
|
35
36
|
NoKernelsDialog,
|
36
37
|
SaveAsDialog,
|
37
38
|
SelectKernelDialog,
|
@@ -112,6 +113,7 @@ class ConsoleApp(BaseApp):
|
|
112
113
|
self.dialogs["no-kernels"] = NoKernelsDialog(self)
|
113
114
|
self.dialogs["change-kernel"] = SelectKernelDialog(self)
|
114
115
|
self.dialogs["shortcuts"] = ShortcutsDialog(self)
|
116
|
+
self.dialogs["confirm"] = ConfirmDialog(self)
|
115
117
|
|
116
118
|
return FloatContainer(
|
117
119
|
DisableMouseOnScroll(
|
@@ -40,7 +40,7 @@ from euporie.core.filters import (
|
|
40
40
|
)
|
41
41
|
from euporie.core.format import LspFormatter
|
42
42
|
from euporie.core.io import edit_in_editor
|
43
|
-
from euporie.core.kernel.
|
43
|
+
from euporie.core.kernel.base import MsgCallbacks
|
44
44
|
from euporie.core.key_binding.registry import (
|
45
45
|
load_registered_bindings,
|
46
46
|
register_bindings,
|
@@ -137,10 +137,7 @@ class Console(KernelTab):
|
|
137
137
|
|
138
138
|
self.container = self.load_container()
|
139
139
|
|
140
|
-
self.kernel.start(cb=self.kernel_started, wait=False)
|
141
|
-
|
142
140
|
self.app.before_render += self.render_outputs
|
143
|
-
|
144
141
|
self.on_advance = Event(self)
|
145
142
|
|
146
143
|
async def load_lsps(self) -> None:
|
@@ -162,9 +159,26 @@ class Console(KernelTab):
|
|
162
159
|
|
163
160
|
lsp.on_exit += lsp_unload
|
164
161
|
|
162
|
+
def post_init_kernel(self) -> None:
|
163
|
+
"""Start the kernel after if has been loaded."""
|
164
|
+
# Load container
|
165
|
+
super().post_init_kernel()
|
166
|
+
|
167
|
+
# Start kernel
|
168
|
+
if self.kernel._status == "stopped":
|
169
|
+
self.kernel.start(cb=self.kernel_started, wait=False)
|
170
|
+
|
165
171
|
def kernel_died(self) -> None:
|
166
|
-
"""Call
|
172
|
+
"""Call if the kernel dies."""
|
167
173
|
log.error("The kernel has died")
|
174
|
+
if confirm := self.app.dialogs.get("confirm"):
|
175
|
+
confirm.show(
|
176
|
+
title="Kernel connection lost",
|
177
|
+
message="The kernel appears to have died\n"
|
178
|
+
"as it can no longer be reached.\n\n"
|
179
|
+
"Do you want to restart the kernel?",
|
180
|
+
cb=self.kernel.restart,
|
181
|
+
)
|
168
182
|
|
169
183
|
async def load_history(self) -> None:
|
170
184
|
"""Load kernel history."""
|
@@ -191,9 +205,7 @@ class Console(KernelTab):
|
|
191
205
|
def validate_input(self, code: str) -> bool:
|
192
206
|
"""Determine if the entered code is ready to run."""
|
193
207
|
assert self.kernel is not None
|
194
|
-
completeness_status = self.kernel.is_complete(code
|
195
|
-
"status", "unknown"
|
196
|
-
)
|
208
|
+
completeness_status = self.kernel.is_complete(code).get("status", "unknown")
|
197
209
|
return not (
|
198
210
|
not code.strip()
|
199
211
|
or completeness_status == "incomplete"
|
@@ -404,12 +416,7 @@ class Console(KernelTab):
|
|
404
416
|
if ((json_cells and cell.id != json_cells[0].id) or i > 0) and (
|
405
417
|
(height_known and rows_above_layout > 0) or not height_known
|
406
418
|
):
|
407
|
-
children.append(
|
408
|
-
Window(
|
409
|
-
height=1,
|
410
|
-
dont_extend_height=True,
|
411
|
-
)
|
412
|
-
)
|
419
|
+
children.append(Window(height=1, dont_extend_height=True))
|
413
420
|
|
414
421
|
# Cell input
|
415
422
|
children.append(
|
@@ -441,9 +448,12 @@ class Console(KernelTab):
|
|
441
448
|
if outputs := cell.outputs:
|
442
449
|
# Add space before an output if last rendered cell did not have outputs
|
443
450
|
# or we are rendering a new output
|
444
|
-
if self.last_rendered is
|
445
|
-
|
446
|
-
|
451
|
+
if self.last_rendered is None or (
|
452
|
+
self.last_rendered is not None
|
453
|
+
and (
|
454
|
+
not self.last_rendered.outputs
|
455
|
+
or cell.execution_count != self.last_rendered.execution_count
|
456
|
+
)
|
447
457
|
):
|
448
458
|
children.append(
|
449
459
|
Window(
|
@@ -1,10 +1,10 @@
|
|
1
1
|
"""This package defines the euporie application and its components."""
|
2
2
|
|
3
3
|
__app_name__ = "euporie"
|
4
|
-
__version__ = "2.8.
|
4
|
+
__version__ = "2.8.7"
|
5
5
|
__logo__ = "⚈"
|
6
6
|
__strapline__ = "Jupyter in the terminal"
|
7
7
|
__author__ = "Josiah Outram Halstead"
|
8
8
|
__email__ = "josiah@halstead.email"
|
9
|
-
__copyright__ = f"©
|
9
|
+
__copyright__ = f"© 2025, {__author__}"
|
10
10
|
__license__ = "MIT"
|
@@ -28,20 +28,6 @@ def _force_quit() -> None:
|
|
28
28
|
Application.exit(get_app())
|
29
29
|
|
30
30
|
|
31
|
-
@add_cmd(aliases=["wq", "x"])
|
32
|
-
def _save_and_quit(event: KeyPressEvent) -> None:
|
33
|
-
"""Save the current tab then quits euporie."""
|
34
|
-
from upath import UPath
|
35
|
-
|
36
|
-
app = get_app()
|
37
|
-
if (tab := get_app().tab) is not None:
|
38
|
-
try:
|
39
|
-
tab._save(UPath(event._arg) if event._arg else None)
|
40
|
-
except NotImplementedError:
|
41
|
-
pass
|
42
|
-
app.exit()
|
43
|
-
|
44
|
-
|
45
31
|
@add_cmd(aliases=["bc"], filter=tab_has_focus, menu_title="Close File")
|
46
32
|
def _close_tab() -> None:
|
47
33
|
"""Close the current tab."""
|
@@ -79,17 +65,14 @@ def _clear_screen() -> None:
|
|
79
65
|
|
80
66
|
|
81
67
|
@add_cmd(hidden=True, aliases=[""])
|
82
|
-
def _go_to(event: KeyPressEvent) -> None:
|
68
|
+
def _go_to(event: KeyPressEvent, index: int = 0) -> None:
|
83
69
|
"""Go to a line or cell by number."""
|
84
|
-
|
85
|
-
idx = int(event._arg or "") - 1
|
86
|
-
except (ValueError, TypeError):
|
87
|
-
return
|
70
|
+
index = max(0, index - 1)
|
88
71
|
if buffer_has_focus():
|
89
72
|
buffer = get_app().current_buffer
|
90
|
-
buffer.cursor_position = len("".join(buffer.text.splitlines(True)[:
|
73
|
+
buffer.cursor_position = len("".join(buffer.text.splitlines(True)[:index]))
|
91
74
|
elif tab_type_has_focus("euporie.notebook.tabs.notebook:Notebook")():
|
92
75
|
from euporie.notebook.tabs.notebook import Notebook
|
93
76
|
|
94
77
|
if isinstance(nb := get_app().tab, Notebook):
|
95
|
-
nb.select(
|
78
|
+
nb.select(index)
|
@@ -520,14 +520,17 @@ class BaseApp(ConfigurableApp, Application, ABC):
|
|
520
520
|
@classmethod
|
521
521
|
def launch(cls) -> None:
|
522
522
|
"""Launch the app."""
|
523
|
+
from prompt_toolkit.utils import in_main_thread
|
524
|
+
|
523
525
|
super().launch()
|
524
526
|
# Run the application
|
525
527
|
with create_app_session(input=cls.load_input(), output=cls.load_output()):
|
526
528
|
# Create an instance of the app and run it
|
527
529
|
app = cls()
|
528
|
-
|
529
|
-
|
530
|
-
|
530
|
+
if in_main_thread():
|
531
|
+
# Handle SIGTERM while the app is running
|
532
|
+
original_sigterm = signal.getsignal(signal.SIGTERM)
|
533
|
+
signal.signal(signal.SIGTERM, app.cleanup)
|
531
534
|
# Set and run the app
|
532
535
|
with set_app(app):
|
533
536
|
try:
|
@@ -535,7 +538,8 @@ class BaseApp(ConfigurableApp, Application, ABC):
|
|
535
538
|
except (EOFError, KeyboardInterrupt):
|
536
539
|
result = None
|
537
540
|
finally:
|
538
|
-
|
541
|
+
if in_main_thread():
|
542
|
+
signal.signal(signal.SIGTERM, original_sigterm)
|
539
543
|
# Shut down any remaining LSP clients at exit
|
540
544
|
app.shutdown_lsps()
|
541
545
|
return result
|
@@ -582,14 +586,16 @@ class BaseApp(ConfigurableApp, Application, ABC):
|
|
582
586
|
path_mime = get_mime(path) or "text/plain"
|
583
587
|
log.debug("File %s has mime type: %s", path, path_mime)
|
584
588
|
|
585
|
-
|
589
|
+
# Use a set to automatically handle duplicates
|
590
|
+
tab_options: set[TabRegistryEntry] = set()
|
586
591
|
for entry in self.tab_registry:
|
587
592
|
for mime_type in entry.mime_types:
|
588
593
|
if PurePath(path_mime).match(mime_type):
|
589
|
-
tab_options.
|
594
|
+
tab_options.add(entry)
|
590
595
|
if path.suffix in entry.file_extensions:
|
591
|
-
tab_options.
|
596
|
+
tab_options.add(entry)
|
592
597
|
|
598
|
+
# Sort by weight (TabRegistryEntry.__lt__ handles this)
|
593
599
|
return sorted(tab_options, reverse=True)
|
594
600
|
|
595
601
|
def get_file_tab(self, path: Path) -> type[Tab] | None:
|
@@ -29,6 +29,7 @@ from euporie.core.key_binding.registry import (
|
|
29
29
|
|
30
30
|
if TYPE_CHECKING:
|
31
31
|
from collections.abc import Iterable
|
32
|
+
from typing import Unpack
|
32
33
|
|
33
34
|
from prompt_toolkit.completion.base import CompleteEvent
|
34
35
|
from prompt_toolkit.document import Document
|
@@ -195,11 +196,13 @@ class CommandBar:
|
|
195
196
|
|
196
197
|
@staticmethod
|
197
198
|
@add_cmd(aliases=["shell"])
|
198
|
-
async def _run_shell_command(
|
199
|
+
async def _run_shell_command(
|
200
|
+
event: KeyPressEvent, *cmd_arg: Unpack[tuple[str]]
|
201
|
+
) -> None:
|
199
202
|
"""Run system command."""
|
200
|
-
|
201
|
-
if
|
202
|
-
await app.run_system_command(
|
203
|
-
|
204
|
-
display_before_text=[("bold", "$ "), ("", f"{
|
203
|
+
command = " ".join(str(x) for x in cmd_arg)
|
204
|
+
if command:
|
205
|
+
await event.app.run_system_command(
|
206
|
+
command,
|
207
|
+
display_before_text=[("bold", "$ "), ("", f"{command}\n")],
|
205
208
|
)
|
@@ -3,6 +3,7 @@
|
|
3
3
|
from __future__ import annotations
|
4
4
|
|
5
5
|
import logging
|
6
|
+
import re
|
6
7
|
from typing import TYPE_CHECKING
|
7
8
|
|
8
9
|
from prompt_toolkit.buffer import Buffer
|
@@ -27,6 +28,7 @@ from euporie.core.key_binding.registry import (
|
|
27
28
|
if TYPE_CHECKING:
|
28
29
|
from prompt_toolkit.filters import FilterOrBool
|
29
30
|
from prompt_toolkit.formatted_text.base import AnyFormattedText
|
31
|
+
from prompt_toolkit.layout.controls import UIControl
|
30
32
|
|
31
33
|
log = logging.getLogger(__name__)
|
32
34
|
|
@@ -119,9 +121,17 @@ def find_searchable_controls(
|
|
119
121
|
search_buffer_control: SearchBufferControl, current_control: BufferControl | None
|
120
122
|
) -> list[BufferControl]:
|
121
123
|
"""Find list of searchable controls and the index of the next control."""
|
122
|
-
|
124
|
+
# If a tab provides a list of buffers to search, use that. Otherwise, trawl the
|
125
|
+
# layout for buffer controls with this as its search control
|
126
|
+
long_list: list[UIControl]
|
127
|
+
if tab := get_app().tab:
|
128
|
+
try:
|
129
|
+
long_list = list(tab.__pt_searchables__())
|
130
|
+
except NotImplementedError:
|
131
|
+
long_list = list(get_app().layout.find_all_controls())
|
123
132
|
next_control_index = 0
|
124
|
-
|
133
|
+
searchable_controls: list[BufferControl] = []
|
134
|
+
for control in long_list:
|
125
135
|
# Find the index of the next searchable control so we can link the search
|
126
136
|
# control to it if the currently focused control is not searchable. This is so
|
127
137
|
# that the next searchable control can be focused when search is completed.
|
@@ -134,6 +144,7 @@ def find_searchable_controls(
|
|
134
144
|
):
|
135
145
|
# Add it to our list
|
136
146
|
searchable_controls.append(control)
|
147
|
+
# Cut list based on current control index
|
137
148
|
searchable_controls = (
|
138
149
|
searchable_controls[next_control_index:]
|
139
150
|
+ searchable_controls[:next_control_index]
|
@@ -332,3 +343,33 @@ def accept_search() -> None:
|
|
332
343
|
search_buffer_control.buffer.append_to_history()
|
333
344
|
# Stop the search
|
334
345
|
stop_search()
|
346
|
+
|
347
|
+
|
348
|
+
@add_cmd()
|
349
|
+
def _replace_all(find_str: str, replace_str: str) -> None:
|
350
|
+
"""Find and replace text in all searchable buffers.
|
351
|
+
|
352
|
+
Args:
|
353
|
+
find_str: String pattern to find (will be converted to regex)
|
354
|
+
replace_str: Replacement string
|
355
|
+
"""
|
356
|
+
# Convert find string to regex pattern
|
357
|
+
pattern = re.compile(find_str)
|
358
|
+
|
359
|
+
# Get searchable controls
|
360
|
+
search_buffer_control, current_control = find_search_control()
|
361
|
+
if search_buffer_control is None:
|
362
|
+
return
|
363
|
+
searchable_controls = find_searchable_controls(
|
364
|
+
search_buffer_control, current_control
|
365
|
+
)
|
366
|
+
|
367
|
+
# Apply replacements to each buffer
|
368
|
+
for control in searchable_controls:
|
369
|
+
if isinstance(control, BufferControl):
|
370
|
+
buffer = control.buffer
|
371
|
+
text = buffer.text
|
372
|
+
new_text = pattern.sub(replace_str, text)
|
373
|
+
if new_text != text:
|
374
|
+
buffer.text = new_text
|
375
|
+
buffer.on_text_changed()
|
@@ -512,6 +512,11 @@ _GRID_CHARS = {
|
|
512
512
|
GridChar(LowerLeftQuarterLine, NoLine, NoLine, UpperRightEighthLine): " ",
|
513
513
|
GridChar(UpperRightQuarterLine, UpperRightEighthLine, NoLine, NoLine): " ",
|
514
514
|
|
515
|
+
GridChar(NoLine, NoLine, UpperRightQuarterLine, UpperRightEighthLine): "▁",
|
516
|
+
GridChar(UpperRightQuarterLine, NoLine, NoLine, UpperRightEighthLine): "▔",
|
517
|
+
GridChar(LowerLeftQuarterLine, UpperRightEighthLine, NoLine, NoLine): "▔",
|
518
|
+
GridChar(NoLine, LowerLeftEighthLine, LowerLeftQuarterLine, NoLine): "▁",
|
519
|
+
|
515
520
|
# LowerLeftQuarterLine
|
516
521
|
GridChar(LowerLeftQuarterLine, NoLine, LowerLeftQuarterLine, NoLine): "▎",
|
517
522
|
GridChar(NoLine, LowerLeftQuarterLine, NoLine, LowerLeftQuarterLine): "▂",
|
@@ -875,9 +880,9 @@ InsetGrid = (
|
|
875
880
|
|
876
881
|
OutsetGrid = (
|
877
882
|
LowerLeftEighthLine.top_edge
|
878
|
-
+
|
883
|
+
+ UpperRightQuarterLine.right_edge
|
879
884
|
+ UpperRightEighthLine.bottom_edge
|
880
|
-
+
|
885
|
+
+ LowerLeftQuarterLine.left_edge
|
881
886
|
+ ThinLine.inner
|
882
887
|
)
|
883
888
|
|
@@ -15,7 +15,7 @@ if TYPE_CHECKING:
|
|
15
15
|
|
16
16
|
from prompt_toolkit.layout.containers import AnyContainer
|
17
17
|
|
18
|
-
from euporie.core.kernel.
|
18
|
+
from euporie.core.kernel.jupyter import JupyterKernel
|
19
19
|
from euporie.core.tabs.kernel import KernelTab
|
20
20
|
from euporie.core.widgets.cell_outputs import OutputParent
|
21
21
|
|
@@ -40,7 +40,7 @@ class CommView:
|
|
40
40
|
"""
|
41
41
|
self.container = container
|
42
42
|
self.setters: dict[str, Callable[..., None]] = dict(setters or {})
|
43
|
-
self.kernel:
|
43
|
+
self.kernel: JupyterKernel | None = None
|
44
44
|
|
45
45
|
def update(self, changes: dict[str, Any]) -> None:
|
46
46
|
"""Update the view to reflect changes in the Comm.
|
@@ -19,7 +19,7 @@ from prompt_toolkit.layout.processors import BeforeInput
|
|
19
19
|
|
20
20
|
from euporie.core.comm.base import Comm, CommView
|
21
21
|
from euporie.core.data_structures import DiBool
|
22
|
-
from euporie.core.kernel.
|
22
|
+
from euporie.core.kernel.jupyter import MsgCallbacks
|
23
23
|
from euporie.core.layout.decor import FocusedStyle
|
24
24
|
from euporie.core.widgets.forms import (
|
25
25
|
Button,
|
@@ -518,7 +518,7 @@ class TextBoxIpyWidgetComm(IpyWidgetComm, metaclass=ABCMeta):
|
|
518
518
|
container,
|
519
519
|
setters={
|
520
520
|
"value": lambda x: setattr(text.buffer, "text", str(x)),
|
521
|
-
"rows": partial(setattr, text.
|
521
|
+
"rows": partial(setattr, text.window, "height"),
|
522
522
|
"placeholder": partial(setattr, text, "placeholder"),
|
523
523
|
"description_allow_html": partial(setattr, labelled_widget, "html"),
|
524
524
|
},
|
@@ -1403,7 +1403,7 @@ class ColorPickerModel(TextBoxIpyWidgetComm):
|
|
1403
1403
|
container,
|
1404
1404
|
setters={
|
1405
1405
|
"value": lambda x: setattr(text.buffer, "text", str(x)),
|
1406
|
-
"rows": partial(setattr, text.
|
1406
|
+
"rows": partial(setattr, text.window, "height"),
|
1407
1407
|
"placeholder": partial(setattr, text, "placeholder"),
|
1408
1408
|
},
|
1409
1409
|
)
|
@@ -41,6 +41,36 @@ if TYPE_CHECKING:
|
|
41
41
|
log = logging.getLogger(__name__)
|
42
42
|
|
43
43
|
|
44
|
+
def parse_args(arg: str) -> list[Any]:
|
45
|
+
"""Parse a command argument string into a list of values.
|
46
|
+
|
47
|
+
Args:
|
48
|
+
arg: The argument string to parse
|
49
|
+
|
50
|
+
Returns:
|
51
|
+
A list of parsed values, with strings for items that couldn't be evaluated
|
52
|
+
"""
|
53
|
+
if not arg:
|
54
|
+
return []
|
55
|
+
|
56
|
+
import ast
|
57
|
+
|
58
|
+
result = []
|
59
|
+
for item in arg.split():
|
60
|
+
try:
|
61
|
+
# Safely evaluate string as a Python literal
|
62
|
+
new_value = ast.literal_eval(item)
|
63
|
+
except (ValueError, SyntaxError):
|
64
|
+
# Keep as string if evaluation fails
|
65
|
+
result.append(item)
|
66
|
+
else:
|
67
|
+
if type(new_value) is str:
|
68
|
+
result.append(item)
|
69
|
+
else:
|
70
|
+
result.append(new_value)
|
71
|
+
return result
|
72
|
+
|
73
|
+
|
44
74
|
class Command:
|
45
75
|
"""Wrap a function so it can be used as a key-binding or a menu item."""
|
46
76
|
|
@@ -110,18 +140,19 @@ class Command:
|
|
110
140
|
|
111
141
|
self.keys: list[tuple[str | Keys, ...]] = []
|
112
142
|
|
113
|
-
def run(self, arg: str
|
143
|
+
def run(self, arg: str = "") -> None:
|
114
144
|
"""Run the command's handler."""
|
115
145
|
if self.filter():
|
116
146
|
app = get_app()
|
117
147
|
result = self.key_handler(
|
118
148
|
KeyPressEvent(
|
119
149
|
key_processor_ref=weakref.ref(app.key_processor),
|
120
|
-
arg=
|
150
|
+
arg=None,
|
121
151
|
key_sequence=[],
|
122
152
|
previous_key_sequence=[],
|
123
153
|
is_repeat=False,
|
124
154
|
),
|
155
|
+
*parse_args(arg),
|
125
156
|
)
|
126
157
|
if isawaitable(result):
|
127
158
|
|
@@ -141,14 +172,19 @@ class Command:
|
|
141
172
|
handler = self.handler
|
142
173
|
sig = signature(handler)
|
143
174
|
|
144
|
-
if sig.parameters:
|
145
|
-
# The handler already accepts a `KeyPressEvent` argument
|
175
|
+
if sig.parameters and next(iter(sig.parameters.keys())) == "event":
|
176
|
+
# The handler already accepts a `KeyPressEvent` argument named "event"
|
177
|
+
# as the first parameter
|
146
178
|
return cast("KeyHandlerCallable", handler)
|
147
179
|
|
180
|
+
# Otherwise we need to wrap in a function which accepts a KeyPressEvent as the
|
181
|
+
# first parameter
|
148
182
|
if iscoroutinefunction(handler):
|
149
183
|
|
150
|
-
async def _key_handler_async(
|
151
|
-
|
184
|
+
async def _key_handler_async(
|
185
|
+
event: KeyPressEvent, *args: Any
|
186
|
+
) -> NotImplementedOrNone:
|
187
|
+
result = cast("CommandHandlerNoArgs", handler)(*args)
|
152
188
|
assert isawaitable(result)
|
153
189
|
return await result
|
154
190
|
|
@@ -156,8 +192,8 @@ class Command:
|
|
156
192
|
|
157
193
|
else:
|
158
194
|
|
159
|
-
def _key_handler(event: KeyPressEvent) -> NotImplementedOrNone:
|
160
|
-
return cast("CommandHandlerNoArgs", handler)()
|
195
|
+
def _key_handler(event: KeyPressEvent, *args: Any) -> NotImplementedOrNone:
|
196
|
+
return cast("CommandHandlerNoArgs", handler)(*args)
|
161
197
|
|
162
198
|
return _key_handler
|
163
199
|
|