euporie 2.8.5__py3-none-any.whl → 2.8.7__py3-none-any.whl
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/console/app.py +2 -0
- euporie/console/tabs/console.py +27 -17
- euporie/core/__init__.py +2 -2
- euporie/core/__main__.py +2 -2
- euporie/core/_settings.py +7 -2
- euporie/core/app/_commands.py +20 -12
- euporie/core/app/_settings.py +34 -4
- euporie/core/app/app.py +31 -18
- euporie/core/bars/command.py +53 -27
- euporie/core/bars/search.py +43 -2
- euporie/core/border.py +7 -2
- euporie/core/comm/base.py +2 -2
- euporie/core/comm/ipywidgets.py +3 -3
- euporie/core/commands.py +44 -24
- euporie/core/completion.py +14 -6
- euporie/core/convert/datum.py +7 -7
- euporie/core/data_structures.py +20 -1
- euporie/core/filters.py +40 -9
- euporie/core/format.py +2 -3
- euporie/core/ft/html.py +47 -40
- euporie/core/graphics.py +199 -31
- euporie/core/history.py +15 -5
- euporie/core/inspection.py +16 -9
- euporie/core/kernel/__init__.py +53 -1
- euporie/core/kernel/base.py +571 -0
- euporie/core/kernel/{client.py → jupyter.py} +173 -430
- euporie/core/kernel/{manager.py → jupyter_manager.py} +4 -3
- euporie/core/kernel/local.py +694 -0
- euporie/core/key_binding/bindings/basic.py +6 -3
- euporie/core/keys.py +26 -25
- euporie/core/layout/cache.py +31 -7
- euporie/core/layout/containers.py +88 -13
- euporie/core/layout/scroll.py +69 -170
- euporie/core/log.py +2 -5
- euporie/core/path.py +61 -13
- euporie/core/style.py +2 -1
- euporie/core/suggest.py +155 -74
- euporie/core/tabs/__init__.py +12 -4
- euporie/core/tabs/_commands.py +76 -0
- euporie/core/tabs/_settings.py +16 -0
- euporie/core/tabs/base.py +89 -9
- euporie/core/tabs/kernel.py +83 -38
- euporie/core/tabs/notebook.py +28 -76
- euporie/core/utils.py +2 -19
- euporie/core/validation.py +8 -8
- euporie/core/widgets/_settings.py +19 -2
- euporie/core/widgets/cell.py +32 -32
- euporie/core/widgets/cell_outputs.py +10 -1
- euporie/core/widgets/dialog.py +60 -76
- euporie/core/widgets/display.py +2 -2
- euporie/core/widgets/forms.py +71 -59
- euporie/core/widgets/inputs.py +7 -4
- euporie/core/widgets/layout.py +281 -93
- euporie/core/widgets/menu.py +56 -16
- euporie/core/widgets/palette.py +3 -1
- euporie/core/widgets/tree.py +86 -76
- euporie/notebook/app.py +35 -16
- euporie/notebook/tabs/display.py +2 -2
- euporie/notebook/tabs/edit.py +11 -46
- euporie/notebook/tabs/json.py +8 -4
- euporie/notebook/tabs/notebook.py +26 -8
- euporie/preview/tabs/notebook.py +17 -13
- euporie/web/__init__.py +1 -0
- euporie/web/tabs/__init__.py +14 -0
- euporie/web/tabs/web.py +30 -5
- euporie/web/widgets/__init__.py +1 -0
- euporie/web/widgets/webview.py +5 -4
- {euporie-2.8.5.dist-info → euporie-2.8.7.dist-info}/METADATA +4 -2
- {euporie-2.8.5.dist-info → euporie-2.8.7.dist-info}/RECORD +74 -68
- {euporie-2.8.5.dist-info → euporie-2.8.7.dist-info}/entry_points.txt +1 -1
- {euporie-2.8.5.dist-info → euporie-2.8.7.dist-info}/licenses/LICENSE +1 -1
- {euporie-2.8.5.data → euporie-2.8.7.data}/data/share/applications/euporie-console.desktop +0 -0
- {euporie-2.8.5.data → euporie-2.8.7.data}/data/share/applications/euporie-notebook.desktop +0 -0
- {euporie-2.8.5.dist-info → euporie-2.8.7.dist-info}/WHEEL +0 -0
euporie/core/graphics.py
CHANGED
@@ -376,8 +376,8 @@ class ItermGraphicControl(GraphicControl):
|
|
376
376
|
return self._format_cache.get(key, render_lines)
|
377
377
|
|
378
378
|
|
379
|
-
class
|
380
|
-
"""
|
379
|
+
class BaseKittyGraphicControl(GraphicControl):
|
380
|
+
"""Base graphic control with common methods for both styles of kitty display."""
|
381
381
|
|
382
382
|
_kitty_image_count: ClassVar[int] = 1
|
383
383
|
|
@@ -451,7 +451,7 @@ class KittyGraphicControl(GraphicControl):
|
|
451
451
|
BoundedWritePosition(0, 0, width=cols, height=rows, bbox=bbox)
|
452
452
|
)
|
453
453
|
self.kitty_image_id = self._kitty_image_count
|
454
|
-
|
454
|
+
self.__class__._kitty_image_count += 1
|
455
455
|
|
456
456
|
while data:
|
457
457
|
chunk, data = data[:4096], data[4096:]
|
@@ -471,24 +471,6 @@ class KittyGraphicControl(GraphicControl):
|
|
471
471
|
self.app.output.flush()
|
472
472
|
self.loaded = True
|
473
473
|
|
474
|
-
def hide_cmd(self) -> str:
|
475
|
-
"""Generate a command to hide the graphic."""
|
476
|
-
return passthrough(
|
477
|
-
self._kitty_cmd(
|
478
|
-
a="d",
|
479
|
-
d="i",
|
480
|
-
i=self.kitty_image_id,
|
481
|
-
q=1,
|
482
|
-
),
|
483
|
-
self.app.config,
|
484
|
-
)
|
485
|
-
|
486
|
-
def hide(self) -> None:
|
487
|
-
"""Hide the graphic from show without deleting it."""
|
488
|
-
if self.kitty_image_id > 0:
|
489
|
-
self.app.output.write_raw(self.hide_cmd())
|
490
|
-
self.app.output.flush()
|
491
|
-
|
492
474
|
def delete(self) -> None:
|
493
475
|
"""Delete the graphic from the terminal."""
|
494
476
|
if self.kitty_image_id > 0:
|
@@ -506,6 +488,22 @@ class KittyGraphicControl(GraphicControl):
|
|
506
488
|
self.app.output.flush()
|
507
489
|
self.loaded = False
|
508
490
|
|
491
|
+
def reset(self) -> None:
|
492
|
+
"""Hide and delete the kitty graphic from the terminal."""
|
493
|
+
self.hide()
|
494
|
+
self.delete()
|
495
|
+
super().reset()
|
496
|
+
|
497
|
+
def close(self) -> None:
|
498
|
+
"""Remove the displayed object entirely."""
|
499
|
+
super().close()
|
500
|
+
if not self.app.leave_graphics():
|
501
|
+
self.delete()
|
502
|
+
|
503
|
+
|
504
|
+
class KittyGraphicControl(BaseKittyGraphicControl):
|
505
|
+
"""A graphic control which displays images using Kitty's graphics protocol."""
|
506
|
+
|
509
507
|
def get_rendered_lines(
|
510
508
|
self, visible_width: int, visible_height: int, wrap_lines: bool = False
|
511
509
|
) -> list[StyleAndTextTuples]:
|
@@ -616,17 +614,181 @@ class KittyGraphicControl(GraphicControl):
|
|
616
614
|
)
|
617
615
|
return self._format_cache.get(key, render_lines)
|
618
616
|
|
619
|
-
def
|
620
|
-
"""
|
621
|
-
|
622
|
-
|
623
|
-
|
617
|
+
def hide_cmd(self) -> str:
|
618
|
+
"""Generate a command to hide the graphic."""
|
619
|
+
return passthrough(
|
620
|
+
self._kitty_cmd(
|
621
|
+
a="d",
|
622
|
+
d="i",
|
623
|
+
i=self.kitty_image_id,
|
624
|
+
q=1,
|
625
|
+
),
|
626
|
+
self.app.config,
|
627
|
+
)
|
628
|
+
|
629
|
+
def hide(self) -> None:
|
630
|
+
"""Hide the graphic from show without deleting it."""
|
631
|
+
if self.kitty_image_id > 0:
|
632
|
+
self.app.output.write_raw(self.hide_cmd())
|
633
|
+
self.app.output.flush()
|
624
634
|
|
625
|
-
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
635
|
+
|
636
|
+
class KittyUnicodeGraphicControl(BaseKittyGraphicControl):
|
637
|
+
"""A graphic control which displays images using Kitty's Unicode placeholder mechanism."""
|
638
|
+
|
639
|
+
PLACEHOLDER = "\U0010eeee" # U+10EEEE placeholder character
|
640
|
+
# fmt: off
|
641
|
+
DIACRITICS = ( # Diacritics for encoding row/column numbers (0-9)
|
642
|
+
"\u0305", "\u030d", "\u030e", "\u0310", "\u0312", "\u033d", "\u033e", "\u033f",
|
643
|
+
"\u0346", "\u034a", "\u034b", "\u034c", "\u0350", "\u0351", "\u0352", "\u0357",
|
644
|
+
"\u035b", "\u0363", "\u0364", "\u0365", "\u0366", "\u0367", "\u0368", "\u0369",
|
645
|
+
"\u036a", "\u036b", "\u036c", "\u036d", "\u036e", "\u036f", "\u0483", "\u0484",
|
646
|
+
"\u0485", "\u0486", "\u0487", "\u0592", "\u0593", "\u0594", "\u0595", "\u0597",
|
647
|
+
"\u0598", "\u0599", "\u059c", "\u059d", "\u059e", "\u059f", "\u05a0", "\u05a1",
|
648
|
+
"\u05a8", "\u05a9", "\u05ab", "\u05ac", "\u05af", "\u05c4", "\u0610", "\u0611",
|
649
|
+
"\u0612", "\u0613", "\u0614", "\u0615", "\u0616", "\u0617", "\u0657", "\u0658",
|
650
|
+
"\u0659", "\u065a", "\u065b", "\u065d", "\u065e", "\u06d6", "\u06d7", "\u06d8",
|
651
|
+
"\u06d9", "\u06da", "\u06db", "\u06dc", "\u06df", "\u06e0", "\u06e1", "\u06e2",
|
652
|
+
"\u06e4", "\u06e7", "\u06e8", "\u06eb", "\u06ec", "\u0730", "\u0732", "\u0733",
|
653
|
+
"\u0735", "\u0736", "\u073a", "\u073d", "\u073f", "\u0740", "\u0741", "\u0743",
|
654
|
+
"\u0745", "\u0747", "\u0749", "\u074a", "\u07eb", "\u07ec", "\u07ed", "\u07ee",
|
655
|
+
"\u07ef", "\u07f0", "\u07f1", "\u07f3", "\u0816", "\u0817", "\u0818", "\u0819",
|
656
|
+
"\u081b", "\u081c", "\u081d", "\u081e", "\u081f", "\u0820", "\u0821", "\u0822",
|
657
|
+
"\u0823", "\u0825", "\u0826", "\u0827", "\u0829", "\u082a", "\u082b", "\u082c",
|
658
|
+
"\u082d", "\u0951", "\u0953", "\u0954", "\u0f82", "\u0f83", "\u0f86", "\u0f87",
|
659
|
+
"\u135d", "\u135e", "\u135f", "\u17dd", "\u193a", "\u1a17", "\u1a75", "\u1a76",
|
660
|
+
"\u1a77", "\u1a78", "\u1a79", "\u1a7a", "\u1a7b", "\u1a7c", "\u1b6b", "\u1b6d",
|
661
|
+
"\u1b6e", "\u1b6f", "\u1b70", "\u1b71", "\u1b72", "\u1b73", "\u1cd0", "\u1cd1",
|
662
|
+
"\u1cd2", "\u1cda", "\u1cdb", "\u1ce0", "\u1dc0", "\u1dc1", "\u1dc3", "\u1dc4",
|
663
|
+
"\u1dc5", "\u1dc6", "\u1dc7", "\u1dc8", "\u1dc9", "\u1dcb", "\u1dcc", "\u1dd1",
|
664
|
+
"\u1dd2", "\u1dd3", "\u1dd4", "\u1dd5", "\u1dd6", "\u1dd7", "\u1dd8", "\u1dd9",
|
665
|
+
"\u1dda", "\u1ddb", "\u1ddc", "\u1ddd", "\u1dde", "\u1ddf", "\u1de0", "\u1de1",
|
666
|
+
"\u1de2", "\u1de3", "\u1de4", "\u1de5", "\u1de6", "\u1dfe", "\u20d0", "\u20d1",
|
667
|
+
"\u20d4", "\u20d5", "\u20d6", "\u20d7", "\u20db", "\u20dc", "\u20e1", "\u20e7",
|
668
|
+
"\u20e9", "\u20f0", "\u2cef", "\u2cf0", "\u2cf1", "\u2de0", "\u2de1", "\u2de2",
|
669
|
+
"\u2de3", "\u2de4", "\u2de5", "\u2de6", "\u2de7", "\u2de8", "\u2de9", "\u2dea",
|
670
|
+
"\u2deb", "\u2dec", "\u2ded", "\u2dee", "\u2def", "\u2df0", "\u2df1", "\u2df2",
|
671
|
+
"\u2df3", "\u2df4", "\u2df5", "\u2df6", "\u2df7", "\u2df8", "\u2df9", "\u2dfa",
|
672
|
+
"\u2dfb", "\u2dfc", "\u2dfd", "\u2dfe", "\u2dff", "\ua66f", "\ua67c", "\ua67d",
|
673
|
+
"\ua6f0", "\ua6f1", "\ua8e0", "\ua8e1", "\ua8e2", "\ua8e3", "\ua8e4", "\ua8e5",
|
674
|
+
"\ua8e6", "\ua8e7", "\ua8e8", "\ua8e9", "\ua8ea", "\ua8eb", "\ua8ec", "\ua8ed",
|
675
|
+
"\ua8ee", "\ua8ef", "\ua8f0", "\ua8f1", "\uaab0", "\uaab2", "\uaab3", "\uaab7",
|
676
|
+
"\uaab8", "\uaabe", "\uaabf", "\uaac1", "\ufe20", "\ufe21", "\ufe22", "\ufe23",
|
677
|
+
"\ufe24", "\ufe25", "\ufe26",
|
678
|
+
"\U00010a0f", "\U00010a38", "\U0001d185", "\U0001d186", "\U0001d187",
|
679
|
+
"\U0001d188", "\U0001d189", "\U0001d1aa", "\U0001d1ab", "\U0001d1ac",
|
680
|
+
"\U0001d1ad", "\U0001d242", "\U0001d243", "\U0001d244",
|
681
|
+
)
|
682
|
+
# fmt: on
|
683
|
+
|
684
|
+
def __init__(
|
685
|
+
self,
|
686
|
+
datum: Datum,
|
687
|
+
scale: float = 0,
|
688
|
+
bbox: DiInt | None = None,
|
689
|
+
) -> None:
|
690
|
+
"""Create a new kitty graphic instance."""
|
691
|
+
super().__init__(datum, scale, bbox)
|
692
|
+
self.placements: set[tuple[int, int]] = set()
|
693
|
+
|
694
|
+
def get_rendered_lines(
|
695
|
+
self, visible_width: int, visible_height: int, wrap_lines: bool = False
|
696
|
+
) -> list[StyleAndTextTuples]:
|
697
|
+
"""Get rendered lines from the cache, or generate them."""
|
698
|
+
bbox = self.bbox
|
699
|
+
|
700
|
+
cell_size_px = self.app.cell_size_px
|
701
|
+
datum = self._datum_pad_cache[(self.datum, *cell_size_px)]
|
702
|
+
px, py = datum.pixel_size()
|
703
|
+
# Fall back to a default pixel size
|
704
|
+
px = px or 100
|
705
|
+
py = py or 100
|
706
|
+
|
707
|
+
d_cols, d_aspect = datum.cell_size()
|
708
|
+
d_rows = d_cols * d_aspect
|
709
|
+
|
710
|
+
total_available_width = visible_width + bbox.left + bbox.right
|
711
|
+
total_available_height = visible_height + bbox.top + bbox.bottom
|
712
|
+
|
713
|
+
# Scale down the graphic to fit in the available space
|
714
|
+
if d_rows > total_available_height or d_cols > total_available_width:
|
715
|
+
if d_rows / total_available_height > d_cols / total_available_width:
|
716
|
+
ratio = min(1, total_available_height / d_rows)
|
717
|
+
else:
|
718
|
+
ratio = min(1, total_available_width / d_cols)
|
719
|
+
else:
|
720
|
+
ratio = 1
|
721
|
+
|
722
|
+
# Calculate the size and cropping bbox at which we want to display the graphic
|
723
|
+
cols = floor(d_cols * ratio)
|
724
|
+
rows = ceil(cols * d_aspect)
|
725
|
+
d_bbox = DiInt(
|
726
|
+
top=self.bbox.top,
|
727
|
+
right=max(0, cols - (total_available_width - self.bbox.right)),
|
728
|
+
bottom=max(0, rows - (total_available_height - self.bbox.bottom)),
|
729
|
+
left=self.bbox.left,
|
730
|
+
)
|
731
|
+
if not self.loaded:
|
732
|
+
self.load(cols=cols, rows=rows, bbox=DiInt(0, 0, 0, 0))
|
733
|
+
|
734
|
+
# Add virtual placement for this size if required
|
735
|
+
if (cols, rows) not in self.placements:
|
736
|
+
cmd = self._kitty_cmd(
|
737
|
+
a="p", # Display a previously transmitted image
|
738
|
+
i=self.kitty_image_id,
|
739
|
+
p=1, # Placement ID
|
740
|
+
U=1, # Create a virtual placement
|
741
|
+
c=cols,
|
742
|
+
r=rows,
|
743
|
+
q=2,
|
744
|
+
)
|
745
|
+
self.app.output.write_raw(passthrough(cmd, self.app.config))
|
746
|
+
self.app.output.flush()
|
747
|
+
self.placements.add((cols, rows))
|
748
|
+
|
749
|
+
def render_lines() -> list[StyleAndTextTuples]:
|
750
|
+
"""Render the lines to display in the control."""
|
751
|
+
ft: StyleAndTextTuples = []
|
752
|
+
|
753
|
+
# Generate placeholder grid
|
754
|
+
row_start = d_bbox.top
|
755
|
+
row_stop = rows - d_bbox.bottom
|
756
|
+
col_start = d_bbox.left
|
757
|
+
col_stop = cols - d_bbox.right
|
758
|
+
placeholder = self.PLACEHOLDER
|
759
|
+
diacritics = self.DIACRITICS
|
760
|
+
for row in range(row_start, row_stop):
|
761
|
+
for col in range(col_start, col_stop):
|
762
|
+
ft.extend(
|
763
|
+
[
|
764
|
+
# We set the ptk-color for the last column so the renderer
|
765
|
+
# knows to change the color back after this gets rendered.
|
766
|
+
("fg:#888" if col == col_stop - 1 else "", " "),
|
767
|
+
(
|
768
|
+
"[ZeroWidthEscape]",
|
769
|
+
# We move the cursor back a cell before writing the
|
770
|
+
# kitty unicode char using a ZWE
|
771
|
+
"\b"
|
772
|
+
# Set the kitty graphic and placement we want to render
|
773
|
+
# by manually setting an 8-bit foregroun color.
|
774
|
+
# The placement ID is set to 1 using underline color.
|
775
|
+
f"\x1b[38;5;{self.kitty_image_id}m\x1b[58;1m"
|
776
|
+
# Writing the unicode char moves the cursor forward
|
777
|
+
# again to where the renderer expects it to be
|
778
|
+
f"{placeholder}{diacritics[row]}{diacritics[col]}",
|
779
|
+
),
|
780
|
+
]
|
781
|
+
)
|
782
|
+
ft.append(("", "\n"))
|
783
|
+
return list(split_lines(ft))
|
784
|
+
|
785
|
+
key = (
|
786
|
+
visible_width,
|
787
|
+
self.app.color_palette,
|
788
|
+
self.app.cell_size_px,
|
789
|
+
bbox,
|
790
|
+
)
|
791
|
+
return self._format_cache.get(key, render_lines)
|
630
792
|
|
631
793
|
|
632
794
|
class NotVisible(Exception):
|
@@ -767,11 +929,17 @@ def select_graphic_control(format_: str) -> type[GraphicControl] | None:
|
|
767
929
|
and (not _in_mplex or (_in_mplex and force_graphics))
|
768
930
|
):
|
769
931
|
useable_graphics_controls.append(KittyGraphicControl)
|
932
|
+
useable_graphics_controls.append(KittyUnicodeGraphicControl)
|
770
933
|
if (
|
771
934
|
preferred_graphics_protocol == "kitty"
|
772
935
|
and KittyGraphicControl in useable_graphics_controls
|
773
936
|
):
|
774
937
|
SelectedGraphicControl = KittyGraphicControl
|
938
|
+
elif (
|
939
|
+
preferred_graphics_protocol == "kitty-unicode"
|
940
|
+
and KittyUnicodeGraphicControl in useable_graphics_controls
|
941
|
+
):
|
942
|
+
SelectedGraphicControl = KittyUnicodeGraphicControl
|
775
943
|
# Tmux now supports sixels (>=3.4)
|
776
944
|
elif (app.term_graphics_sixel or force_graphics) and find_route(
|
777
945
|
format_, "sixel"
|
euporie/core/history.py
CHANGED
@@ -9,8 +9,9 @@ from prompt_toolkit.history import History
|
|
9
9
|
|
10
10
|
if TYPE_CHECKING:
|
11
11
|
from collections.abc import AsyncGenerator, Iterable
|
12
|
+
from typing import Callable
|
12
13
|
|
13
|
-
from euporie.core.kernel.
|
14
|
+
from euporie.core.kernel.base import BaseKernel
|
14
15
|
|
15
16
|
log = logging.getLogger(__name__)
|
16
17
|
|
@@ -18,15 +19,24 @@ log = logging.getLogger(__name__)
|
|
18
19
|
class KernelHistory(History):
|
19
20
|
"""Load the kernel's command history."""
|
20
21
|
|
21
|
-
def __init__(
|
22
|
+
def __init__(
|
23
|
+
self, kernel: BaseKernel | Callable[[], BaseKernel], n: int = 1000
|
24
|
+
) -> None:
|
22
25
|
"""Create a new instance of the kernel history loader."""
|
23
26
|
super().__init__()
|
24
|
-
self.
|
27
|
+
self._kernel = kernel
|
25
28
|
# How many items to load
|
26
29
|
self.n = n
|
27
30
|
self.n_loaded = 0
|
28
31
|
self.loading = False
|
29
32
|
|
33
|
+
@property
|
34
|
+
def kernel(self) -> BaseKernel:
|
35
|
+
"""Return the current kernel."""
|
36
|
+
if callable(self._kernel):
|
37
|
+
return self._kernel()
|
38
|
+
return self._kernel
|
39
|
+
|
30
40
|
async def load(self) -> AsyncGenerator[str, None]:
|
31
41
|
"""Load the history and yield all entries, most recent history first.
|
32
42
|
|
@@ -39,9 +49,9 @@ class KernelHistory(History):
|
|
39
49
|
Yields:
|
40
50
|
Each history string
|
41
51
|
"""
|
42
|
-
if not self.loading and not self._loaded and self.kernel
|
52
|
+
if not self.loading and not self._loaded and self.kernel:
|
43
53
|
self.loading = True
|
44
|
-
items = await self.kernel.
|
54
|
+
items = await self.kernel.history_async(n=self.n, hist_access_type="tail")
|
45
55
|
if items:
|
46
56
|
self._loaded_strings = [item[2] for item in reversed(items)]
|
47
57
|
# Remove sequential duplicates
|
euporie/core/inspection.py
CHANGED
@@ -13,7 +13,7 @@ if TYPE_CHECKING:
|
|
13
13
|
|
14
14
|
from prompt_toolkit.document import Document
|
15
15
|
|
16
|
-
from euporie.core.kernel.
|
16
|
+
from euporie.core.kernel.base import BaseKernel
|
17
17
|
from euporie.core.lsp import LspClient
|
18
18
|
|
19
19
|
|
@@ -27,22 +27,29 @@ class Inspector(metaclass=ABCMeta):
|
|
27
27
|
|
28
28
|
|
29
29
|
class KernelInspector(Inspector):
|
30
|
-
"""Inspector which retrieves contextual help from a
|
30
|
+
"""Inspector which retrieves contextual help from a kernel."""
|
31
31
|
|
32
|
-
def __init__(self, kernel:
|
33
|
-
"""Initialize a new inspector which queries a
|
34
|
-
self.
|
32
|
+
def __init__(self, kernel: BaseKernel | Callable[[], BaseKernel]) -> None:
|
33
|
+
"""Initialize a new inspector which queries a kernel."""
|
34
|
+
self._kernel = kernel
|
35
|
+
|
36
|
+
@property
|
37
|
+
def kernel(self) -> BaseKernel:
|
38
|
+
"""Return the current kernel."""
|
39
|
+
if callable(self._kernel):
|
40
|
+
return self._kernel()
|
41
|
+
return self._kernel
|
35
42
|
|
36
43
|
async def get_context(self, document: Document, auto: bool) -> dict[str, Any]:
|
37
44
|
"""Request contextual help from the kernel."""
|
38
|
-
return await self.kernel.
|
45
|
+
return await self.kernel.inspect_async(document.text, document.cursor_position)
|
39
46
|
|
40
47
|
|
41
48
|
class LspInspector(Inspector):
|
42
49
|
"""Inspector which retrieves contextual help from a Language Server."""
|
43
50
|
|
44
51
|
def __init__(self, lsp: LspClient, path: Path) -> None:
|
45
|
-
"""Initialize a new inspector which queries a
|
52
|
+
"""Initialize a new inspector which queries a kernel."""
|
46
53
|
self.lsp = lsp
|
47
54
|
self.path = path
|
48
55
|
|
@@ -56,12 +63,12 @@ class LspInspector(Inspector):
|
|
56
63
|
|
57
64
|
|
58
65
|
class FirstInspector(Inspector):
|
59
|
-
"""Return results of the first inspector to
|
66
|
+
"""Return results of the first inspector to respond."""
|
60
67
|
|
61
68
|
def __init__(
|
62
69
|
self, inspectors: Sequence[Inspector] | Callable[[], Sequence[Inspector]]
|
63
70
|
) -> None:
|
64
|
-
"""Initialize a new inspector which queries a
|
71
|
+
"""Initialize a new inspector which queries a kernel."""
|
65
72
|
self.inspectors = inspectors
|
66
73
|
|
67
74
|
async def get_context(self, document: Document, auto: bool) -> dict[str, Any]:
|
euporie/core/kernel/__init__.py
CHANGED
@@ -1 +1,53 @@
|
|
1
|
-
"""Concerns the interaction with
|
1
|
+
"""Concerns the interaction with kernels."""
|
2
|
+
|
3
|
+
from __future__ import annotations
|
4
|
+
|
5
|
+
from importlib.util import find_spec
|
6
|
+
from pkgutil import resolve_name
|
7
|
+
from typing import TYPE_CHECKING
|
8
|
+
|
9
|
+
if TYPE_CHECKING:
|
10
|
+
from typing import Any, Literal
|
11
|
+
|
12
|
+
from euporie.core.kernel.base import BaseKernel, KernelInfo, MsgCallbacks
|
13
|
+
from euporie.core.tabs.kernel import KernelTab
|
14
|
+
|
15
|
+
KERNEL_REGISTRY = {
|
16
|
+
"local": "euporie.core.kernel.local:LocalPythonKernel",
|
17
|
+
}
|
18
|
+
if find_spec("jupyter_client"):
|
19
|
+
KERNEL_REGISTRY["jupyter"] = "euporie.core.kernel.jupyter:JupyterKernel"
|
20
|
+
|
21
|
+
|
22
|
+
def list_kernels() -> list[KernelInfo]:
|
23
|
+
"""Get specifications for all available kernel types.
|
24
|
+
|
25
|
+
Returns:
|
26
|
+
A dictionary mapping kernel type names to their specifications.
|
27
|
+
"""
|
28
|
+
return [
|
29
|
+
variant
|
30
|
+
for type_path in KERNEL_REGISTRY.values()
|
31
|
+
for variant in resolve_name(type_path).variants()
|
32
|
+
]
|
33
|
+
|
34
|
+
|
35
|
+
def create_kernel(
|
36
|
+
type_name: Literal["jupyter", "local"],
|
37
|
+
kernel_tab: KernelTab,
|
38
|
+
default_callbacks: MsgCallbacks | None = None,
|
39
|
+
allow_stdin: bool = False,
|
40
|
+
**kwargs: Any,
|
41
|
+
) -> BaseKernel:
|
42
|
+
"""Create and return appropriate kernel instance."""
|
43
|
+
type_path = KERNEL_REGISTRY.get(type_name)
|
44
|
+
if type_path is not None:
|
45
|
+
type_class = resolve_name(type_path)
|
46
|
+
return type_class(
|
47
|
+
kernel_tab=kernel_tab,
|
48
|
+
default_callbacks=default_callbacks,
|
49
|
+
allow_stdin=allow_stdin,
|
50
|
+
**kwargs,
|
51
|
+
)
|
52
|
+
else:
|
53
|
+
raise ValueError(f"Unknown kernel type: {type_name}")
|