euporie 2.8.2__py3-none-any.whl → 2.8.4__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/tabs/console.py +227 -104
- euporie/core/__init__.py +1 -1
- euporie/core/__main__.py +1 -1
- euporie/core/app.py +31 -18
- euporie/core/clipboard.py +1 -1
- euporie/core/comm/ipywidgets.py +5 -5
- euporie/core/commands.py +1 -1
- euporie/core/config.py +4 -4
- euporie/core/convert/datum.py +4 -1
- euporie/core/convert/registry.py +7 -2
- euporie/core/filters.py +3 -1
- euporie/core/ft/html.py +2 -4
- euporie/core/graphics.py +6 -6
- euporie/core/kernel.py +56 -32
- euporie/core/key_binding/bindings/__init__.py +2 -1
- euporie/core/key_binding/bindings/mouse.py +24 -22
- euporie/core/key_binding/bindings/vi.py +46 -0
- euporie/core/layout/cache.py +33 -23
- euporie/core/layout/containers.py +235 -73
- euporie/core/layout/decor.py +3 -3
- euporie/core/layout/print.py +14 -2
- euporie/core/layout/scroll.py +15 -21
- euporie/core/margins.py +59 -30
- euporie/core/style.py +7 -5
- euporie/core/tabs/base.py +32 -0
- euporie/core/tabs/notebook.py +6 -3
- euporie/core/terminal.py +12 -17
- euporie/core/utils.py +2 -4
- euporie/core/widgets/cell.py +64 -109
- euporie/core/widgets/dialog.py +25 -20
- euporie/core/widgets/file_browser.py +3 -3
- euporie/core/widgets/forms.py +8 -7
- euporie/core/widgets/inputs.py +21 -9
- euporie/core/widgets/layout.py +5 -5
- euporie/core/widgets/status.py +3 -3
- euporie/hub/app.py +7 -3
- euporie/notebook/app.py +68 -47
- euporie/notebook/tabs/log.py +1 -1
- euporie/notebook/tabs/notebook.py +5 -3
- euporie/preview/app.py +3 -0
- euporie/preview/tabs/notebook.py +9 -14
- euporie/web/tabs/web.py +0 -1
- {euporie-2.8.2.dist-info → euporie-2.8.4.dist-info}/METADATA +5 -5
- {euporie-2.8.2.dist-info → euporie-2.8.4.dist-info}/RECORD +49 -48
- {euporie-2.8.2.data → euporie-2.8.4.data}/data/share/applications/euporie-console.desktop +0 -0
- {euporie-2.8.2.data → euporie-2.8.4.data}/data/share/applications/euporie-notebook.desktop +0 -0
- {euporie-2.8.2.dist-info → euporie-2.8.4.dist-info}/WHEEL +0 -0
- {euporie-2.8.2.dist-info → euporie-2.8.4.dist-info}/entry_points.txt +0 -0
- {euporie-2.8.2.dist-info → euporie-2.8.4.dist-info}/licenses/LICENSE +0 -0
euporie/core/margins.py
CHANGED
@@ -9,9 +9,11 @@ from typing import TYPE_CHECKING, cast
|
|
9
9
|
|
10
10
|
from prompt_toolkit.data_structures import Point
|
11
11
|
from prompt_toolkit.filters import FilterOrBool, to_filter
|
12
|
+
from prompt_toolkit.layout.containers import ScrollOffsets, WindowRenderInfo
|
12
13
|
from prompt_toolkit.layout.controls import FormattedTextControl
|
13
14
|
from prompt_toolkit.layout.dimension import Dimension
|
14
15
|
from prompt_toolkit.layout.margins import Margin
|
16
|
+
from prompt_toolkit.layout.screen import WritePosition
|
15
17
|
from prompt_toolkit.mouse_events import MouseButton, MouseEventType
|
16
18
|
from prompt_toolkit.mouse_events import MouseEvent as PtkMouseEvent
|
17
19
|
|
@@ -27,10 +29,10 @@ if TYPE_CHECKING:
|
|
27
29
|
KeyBindingsBase,
|
28
30
|
NotImplementedOrNone,
|
29
31
|
)
|
30
|
-
from prompt_toolkit.layout.containers import Container
|
32
|
+
from prompt_toolkit.layout.containers import Container
|
31
33
|
from prompt_toolkit.layout.controls import UIContent
|
32
34
|
from prompt_toolkit.layout.mouse_handlers import MouseHandlers
|
33
|
-
from prompt_toolkit.layout.screen import Screen
|
35
|
+
from prompt_toolkit.layout.screen import Screen
|
34
36
|
|
35
37
|
from euporie.core.diagnostics import Report
|
36
38
|
|
@@ -49,9 +51,9 @@ class ClickableMargin(Margin, metaclass=ABCMeta):
|
|
49
51
|
|
50
52
|
write_position: WritePosition | None
|
51
53
|
|
52
|
-
def
|
54
|
+
def set_margin_window(self, margin_window: Window) -> None:
|
53
55
|
"""Set the write position of the menu."""
|
54
|
-
self.
|
56
|
+
self.margin_window = margin_window
|
55
57
|
|
56
58
|
|
57
59
|
class MarginContainer(Window):
|
@@ -65,6 +67,9 @@ class MarginContainer(Window):
|
|
65
67
|
self.content = FormattedTextControl(self.create_fragments)
|
66
68
|
self.always_hide_cursor = to_filter(True)
|
67
69
|
|
70
|
+
if isinstance(self.margin, ClickableMargin):
|
71
|
+
self.margin.set_margin_window(self)
|
72
|
+
|
68
73
|
def create_fragments(self) -> StyleAndTextTuples:
|
69
74
|
"""Generate text fragments to display."""
|
70
75
|
return self.margin.create_margin(
|
@@ -102,8 +107,6 @@ class MarginContainer(Window):
|
|
102
107
|
) -> None:
|
103
108
|
"""Write the actual content to the screen."""
|
104
109
|
self.write_position = write_position
|
105
|
-
if isinstance(self.margin, ClickableMargin):
|
106
|
-
self.margin.set_write_position(write_position)
|
107
110
|
|
108
111
|
margin_content: UIContent = self.content.create_content(
|
109
112
|
write_position.width + 1, write_position.height
|
@@ -112,6 +115,21 @@ class MarginContainer(Window):
|
|
112
115
|
margin_content, screen, write_position, 0, write_position.width
|
113
116
|
)
|
114
117
|
|
118
|
+
self.render_info = WindowRenderInfo(
|
119
|
+
window=self,
|
120
|
+
ui_content=margin_content,
|
121
|
+
horizontal_scroll=0,
|
122
|
+
vertical_scroll=0,
|
123
|
+
window_width=write_position.width,
|
124
|
+
window_height=write_position.height,
|
125
|
+
configured_scroll_offsets=ScrollOffsets(),
|
126
|
+
visible_line_to_row_col=visible_line_to_row_col,
|
127
|
+
rowcol_to_yx=rowcol_to_yx,
|
128
|
+
x_offset=write_position.xpos,
|
129
|
+
y_offset=write_position.ypos,
|
130
|
+
wrap_lines=False,
|
131
|
+
)
|
132
|
+
|
115
133
|
# Set mouse handlers.
|
116
134
|
def mouse_handler(mouse_event: PtkMouseEvent) -> NotImplementedOrNone:
|
117
135
|
"""Turn screen coordinates into line coordinates."""
|
@@ -164,6 +182,8 @@ class MarginContainer(Window):
|
|
164
182
|
handler=mouse_handler,
|
165
183
|
)
|
166
184
|
|
185
|
+
screen.visible_windows_to_write_positions[self] = write_position
|
186
|
+
|
167
187
|
def is_modal(self) -> bool:
|
168
188
|
"""When this container is modal."""
|
169
189
|
return False
|
@@ -214,8 +234,7 @@ class ScrollbarMargin(ClickableMargin):
|
|
214
234
|
self.thumb_top = 0.0
|
215
235
|
self.thumb_size = 0.0
|
216
236
|
|
217
|
-
self.
|
218
|
-
self.write_position: WritePosition | None = None
|
237
|
+
self.target_render_info: WindowRenderInfo | None = None
|
219
238
|
|
220
239
|
def get_width(self, get_ui_content: Callable[[], UIContent]) -> int:
|
221
240
|
"""Return the scrollbar width: always 1."""
|
@@ -226,13 +245,11 @@ class ScrollbarMargin(ClickableMargin):
|
|
226
245
|
window_render_info: WindowRenderInfo | None,
|
227
246
|
width: int,
|
228
247
|
height: int,
|
229
|
-
margin_render_info: WindowRenderInfo | None = None,
|
230
248
|
) -> StyleAndTextTuples:
|
231
249
|
"""Create the margin's formatted text."""
|
232
250
|
result: StyleAndTextTuples = []
|
233
251
|
|
234
|
-
self.
|
235
|
-
self.margin_render_info = margin_render_info
|
252
|
+
self.target_render_info = window_render_info
|
236
253
|
|
237
254
|
# If this is the first time the target is being drawn, it may not yet have a
|
238
255
|
# render_info yet. Thus, invalidate the app so we can immediately redraw the
|
@@ -400,11 +417,11 @@ class ScrollbarMargin(ClickableMargin):
|
|
400
417
|
:py:const:`None` is returned when the mouse event is handled successfully
|
401
418
|
|
402
419
|
"""
|
403
|
-
|
404
|
-
if
|
420
|
+
target_render_info = self.target_render_info
|
421
|
+
if target_render_info is None:
|
405
422
|
return NotImplemented
|
406
423
|
|
407
|
-
content_height =
|
424
|
+
content_height = target_render_info.content_height
|
408
425
|
if isinstance(mouse_event, MouseEvent):
|
409
426
|
cell_position = mouse_event.cell_position
|
410
427
|
else:
|
@@ -413,9 +430,9 @@ class ScrollbarMargin(ClickableMargin):
|
|
413
430
|
|
414
431
|
# Handle scroll events on the scrollbar
|
415
432
|
if mouse_event.event_type == MouseEventType.SCROLL_UP:
|
416
|
-
|
433
|
+
target_render_info.window._scroll_up()
|
417
434
|
elif mouse_event.event_type == MouseEventType.SCROLL_DOWN:
|
418
|
-
|
435
|
+
target_render_info.window._scroll_down()
|
419
436
|
|
420
437
|
# Mouse drag events
|
421
438
|
elif self.dragging and mouse_event.event_type == MouseEventType.MOUSE_MOVE:
|
@@ -425,8 +442,8 @@ class ScrollbarMargin(ClickableMargin):
|
|
425
442
|
+ 0.5
|
426
443
|
)
|
427
444
|
# Use the window's current vertical scroll as it may have changed since the
|
428
|
-
#
|
429
|
-
window =
|
445
|
+
# target_render_info was generated
|
446
|
+
window = target_render_info.window
|
430
447
|
delta = window.vertical_scroll - target_scroll
|
431
448
|
|
432
449
|
if isinstance(window, Window):
|
@@ -435,7 +452,7 @@ class ScrollbarMargin(ClickableMargin):
|
|
435
452
|
func()
|
436
453
|
# Hack to speed up scrolling on the :py:`ScrollingContainer`
|
437
454
|
elif hasattr(window, "scrolling"):
|
438
|
-
setattr(
|
455
|
+
setattr(target_render_info.window, "scrolling", delta) # noqa: B010
|
439
456
|
|
440
457
|
# Mouse down events
|
441
458
|
elif mouse_event.event_type == MouseEventType.MOUSE_DOWN:
|
@@ -443,20 +460,30 @@ class ScrollbarMargin(ClickableMargin):
|
|
443
460
|
arrows = self.display_arrows()
|
444
461
|
if arrows and int(row) == 0:
|
445
462
|
offset = -1
|
446
|
-
elif arrows and int(row) ==
|
463
|
+
elif arrows and int(row) == target_render_info.window_height - 1:
|
447
464
|
offset = 1
|
448
465
|
# Scroll up or down one page if clicking on the background
|
449
466
|
elif row < self.thumb_top + 1 or self.thumb_top + 1 + self.thumb_size < row:
|
450
467
|
direction = (row < (self.thumb_top + self.thumb_size // 2)) * -2 + 1
|
451
|
-
offset = direction *
|
468
|
+
offset = direction * target_render_info.window_height
|
452
469
|
# We are on the scroll button - start a drag event if this is not a
|
453
470
|
# repeated mouse event
|
454
471
|
elif not repeated:
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
459
|
-
|
472
|
+
# Restrict mouse events to the scrollbar's area. Recalculate the area
|
473
|
+
# based on the margin's window's render_info, in case this is not the
|
474
|
+
# main screen
|
475
|
+
if margin_render_info := self.margin_window.render_info:
|
476
|
+
y_min, x_min = min(margin_render_info._rowcol_to_yx.values())
|
477
|
+
y_max, x_max = max(margin_render_info._rowcol_to_yx.values())
|
478
|
+
get_app().mouse_limits = WritePosition(
|
479
|
+
xpos=x_min,
|
480
|
+
ypos=y_min,
|
481
|
+
width=x_max - x_min + 1,
|
482
|
+
height=y_max - y_min + 1,
|
483
|
+
)
|
484
|
+
self.dragging = True
|
485
|
+
self.drag_start_scroll = target_render_info.vertical_scroll
|
486
|
+
self.drag_start_offset = row - self.thumb_top
|
460
487
|
return None
|
461
488
|
# Otherwise this is a click on the centre scroll button - do nothing
|
462
489
|
else:
|
@@ -465,9 +492,9 @@ class ScrollbarMargin(ClickableMargin):
|
|
465
492
|
if mouse_event.button == MouseButton.LEFT:
|
466
493
|
func = None
|
467
494
|
if offset < 0:
|
468
|
-
func =
|
495
|
+
func = target_render_info.window._scroll_up
|
469
496
|
elif offset > 0:
|
470
|
-
func =
|
497
|
+
func = target_render_info.window._scroll_down
|
471
498
|
if func is not None:
|
472
499
|
# Scroll the window multiple times to scroll by the offset
|
473
500
|
for _ in range(abs(int(offset))):
|
@@ -517,7 +544,7 @@ class NumberedMargin(Margin):
|
|
517
544
|
def get_width(self, get_ui_content: Callable[[], UIContent]) -> int:
|
518
545
|
"""Return the width of the margin."""
|
519
546
|
line_count = get_ui_content().line_count
|
520
|
-
return len("
|
547
|
+
return len(f"{line_count}") + 2
|
521
548
|
|
522
549
|
def create_margin(
|
523
550
|
self, window_render_info: WindowRenderInfo, width: int, height: int
|
@@ -561,8 +588,10 @@ class NumberedMargin(Margin):
|
|
561
588
|
style = f"{self_style} class:line-number.current"
|
562
589
|
else:
|
563
590
|
style = self_style
|
591
|
+
if last_lineno is None and lineno == 0 and not multiline:
|
592
|
+
linestr = ">"
|
564
593
|
# Only display line number if this line is not a continuation of the previous line.
|
565
|
-
|
594
|
+
elif lineno != last_lineno:
|
566
595
|
linestr = str(lineno + 1).rjust(width - 2)
|
567
596
|
else:
|
568
597
|
linestr = " " * (width - 2)
|
euporie/core/style.py
CHANGED
@@ -207,9 +207,9 @@ IPYWIDGET_STYLE = [
|
|
207
207
|
class ColorPaletteColor:
|
208
208
|
"""A representation of a color with adjustment methods."""
|
209
209
|
|
210
|
-
_cache: SimpleCache[
|
211
|
-
|
212
|
-
|
210
|
+
_cache: SimpleCache[tuple[str, float, float, float, bool], ColorPaletteColor] = (
|
211
|
+
SimpleCache()
|
212
|
+
)
|
213
213
|
|
214
214
|
def __init__(self, base: str, _base_override: str = "") -> None:
|
215
215
|
"""Create a new color.
|
@@ -496,8 +496,10 @@ def build_style(
|
|
496
496
|
"cell border edit": f"fg:{cp.hl.adjust(hue=-0.3333, rel=False)}",
|
497
497
|
"cell input prompt": "fg:blue",
|
498
498
|
"cell output prompt": "fg:red",
|
499
|
-
"cell show outputs": "bg
|
500
|
-
"cell show inputs": "bg
|
499
|
+
"cell show outputs": f"fg:{cp.fg.more(0.5)} bg:{cp.bg.more(0.05)}",
|
500
|
+
"cell show inputs": f"fg:{cp.fg.more(0.5)} bg:{cp.bg.more(0.05)}",
|
501
|
+
"cell show inputs border": f"fg:{cp.bg.darker(0.1)}",
|
502
|
+
"cell show outputs border": f"fg:{cp.bg.darker(0.1)}",
|
501
503
|
# Scrollbars
|
502
504
|
"scrollbar": f"fg:{cp.bg.more(0.75)} bg:{cp.bg.more(0.15)}",
|
503
505
|
"scrollbar.background": f"fg:{cp.bg.more(0.75)} bg:{cp.bg.more(0.15)}",
|
euporie/core/tabs/base.py
CHANGED
@@ -584,3 +584,35 @@ class KernelTab(Tab, metaclass=ABCMeta):
|
|
584
584
|
When set, execution timing data will be recorded in cell metadata.
|
585
585
|
""",
|
586
586
|
)
|
587
|
+
|
588
|
+
add_setting(
|
589
|
+
name="show_remote_inputs",
|
590
|
+
flags=["--show-remote-inputs"],
|
591
|
+
type_=bool,
|
592
|
+
help_="Display inputs sent to the kernel by other clients",
|
593
|
+
default=True,
|
594
|
+
description="""
|
595
|
+
If set to `True`, all code input sent to the kernel by any client will be
|
596
|
+
displayed.
|
597
|
+
|
598
|
+
If set to `False`, only inputs sent to the kernel by the current instance
|
599
|
+
of euporie will be displayed, and all other inputs will be ignored.
|
600
|
+
|
601
|
+
""",
|
602
|
+
)
|
603
|
+
|
604
|
+
add_setting(
|
605
|
+
name="show_remote_outputs",
|
606
|
+
flags=["--show-remote-outputs"],
|
607
|
+
type_=bool,
|
608
|
+
help_="Display kernel outputs triggered by other clients",
|
609
|
+
default=True,
|
610
|
+
description="""
|
611
|
+
If set to `False`, only outputs generated by code input from the current
|
612
|
+
instance of euporie will be displayed, and all other outputs will be
|
613
|
+
ignored.
|
614
|
+
|
615
|
+
If set to `True`, all outputs generated by the kernel will be
|
616
|
+
displayed.
|
617
|
+
""",
|
618
|
+
)
|
euporie/core/tabs/notebook.py
CHANGED
@@ -75,7 +75,7 @@ class BaseNotebook(KernelTab, metaclass=ABCMeta):
|
|
75
75
|
prompt, password
|
76
76
|
),
|
77
77
|
"set_execution_count": lambda n: self.cell.set_execution_count(n),
|
78
|
-
"add_output":
|
78
|
+
"add_output": self.new_output_default,
|
79
79
|
"clear_output": lambda wait: self.cell.clear_output(wait),
|
80
80
|
"set_metadata": lambda path, data: self.cell.set_metadata(path, data),
|
81
81
|
"set_status": self.set_status,
|
@@ -116,12 +116,10 @@ class BaseNotebook(KernelTab, metaclass=ABCMeta):
|
|
116
116
|
prev = self.container
|
117
117
|
self.container = self.load_container()
|
118
118
|
self.loaded = True
|
119
|
-
|
120
119
|
# Update the focus if the old container had focus
|
121
120
|
if self.app.layout.has_focus(prev):
|
122
121
|
self.focus()
|
123
122
|
|
124
|
-
self.app.invalidate()
|
125
123
|
# Load widgets
|
126
124
|
self.load_widgets_from_metadata()
|
127
125
|
|
@@ -385,6 +383,11 @@ class BaseNotebook(KernelTab, metaclass=ABCMeta):
|
|
385
383
|
done=cell.ran,
|
386
384
|
)
|
387
385
|
|
386
|
+
def new_output_default(self, output_json: dict[str, Any], own: bool) -> None:
|
387
|
+
"""Add a new output without a cell to the currently selected cell."""
|
388
|
+
if self.app.config.show_remote_outputs:
|
389
|
+
self.cell.add_output(output_json, own)
|
390
|
+
|
388
391
|
def load_widgets_from_metadata(self) -> None:
|
389
392
|
"""Load widgets from state saved in notebook metadata."""
|
390
393
|
for comm_id, comm_data in (
|
euporie/core/terminal.py
CHANGED
@@ -75,6 +75,7 @@ class TerminalQuery:
|
|
75
75
|
cache = False
|
76
76
|
cmd = ""
|
77
77
|
pattern: re.Pattern | None = None
|
78
|
+
run_at_startup: bool = True
|
78
79
|
|
79
80
|
def __init__(self, input_: Input, output: Output, config: Config) -> None:
|
80
81
|
"""Create a new instance of the terminal query."""
|
@@ -257,13 +258,11 @@ class KittyGraphicsStatus(TerminalQuery):
|
|
257
258
|
|
258
259
|
def verify(self, data: str) -> bool:
|
259
260
|
"""Verify the terminal response means kitty graphics are supported."""
|
260
|
-
|
261
|
+
return bool(
|
261
262
|
(match := self.pattern.match(data))
|
262
263
|
and (values := match.groupdict())
|
263
264
|
and values.get("status") == "OK"
|
264
|
-
)
|
265
|
-
return True
|
266
|
-
return False
|
265
|
+
)
|
267
266
|
|
268
267
|
|
269
268
|
class SixelGraphicsStatus(TerminalQuery):
|
@@ -279,13 +278,11 @@ class SixelGraphicsStatus(TerminalQuery):
|
|
279
278
|
|
280
279
|
def verify(self, data: str) -> bool:
|
281
280
|
"""Verify the terminal response means sixel graphics are supported."""
|
282
|
-
|
281
|
+
return bool(
|
283
282
|
(match := self.pattern.match(data))
|
284
283
|
and (values := match.groupdict())
|
285
284
|
and values.get("sixel")
|
286
|
-
)
|
287
|
-
return True
|
288
|
-
return False
|
285
|
+
)
|
289
286
|
|
290
287
|
|
291
288
|
class ItermGraphicsStatus(TerminalQuery):
|
@@ -301,14 +298,12 @@ class ItermGraphicsStatus(TerminalQuery):
|
|
301
298
|
|
302
299
|
def verify(self, data: str) -> bool:
|
303
300
|
"""Verify iterm graphics are supported by the terminal."""
|
304
|
-
|
301
|
+
return bool(
|
305
302
|
(match := self.pattern.match(data))
|
306
303
|
and (values := match.groupdict())
|
307
304
|
and (term := values.get("term"))
|
308
|
-
and
|
309
|
-
)
|
310
|
-
return True
|
311
|
-
return False
|
305
|
+
and term.startswith(("WezTerm", "Konsole", "mlterm"))
|
306
|
+
)
|
312
307
|
|
313
308
|
|
314
309
|
class DepthOfColor(TerminalQuery):
|
@@ -362,9 +357,7 @@ class CsiUStatus(TerminalQuery):
|
|
362
357
|
|
363
358
|
def verify(self, data: str) -> bool:
|
364
359
|
"""Verify the terminal responds."""
|
365
|
-
|
366
|
-
return True
|
367
|
-
return False
|
360
|
+
return bool((match := self.pattern.match(data)) and match)
|
368
361
|
|
369
362
|
|
370
363
|
class ClipboardData(TerminalQuery):
|
@@ -374,6 +367,7 @@ class ClipboardData(TerminalQuery):
|
|
374
367
|
cache = False
|
375
368
|
cmd = "\x1b]52;c;?\x1b\\"
|
376
369
|
pattern = re.compile(r"^\x1b\]52;(?:c|p)?;(?P<data>[A-Za-z0-9+/=]+)\x1b\\\Z")
|
370
|
+
run_at_startup = False
|
377
371
|
|
378
372
|
def verify(self, data: str) -> str:
|
379
373
|
"""Verify the terminal responds."""
|
@@ -453,7 +447,8 @@ class TerminalInfo:
|
|
453
447
|
# Ensure line wrapping is off before sending queries
|
454
448
|
self.output.disable_autowrap()
|
455
449
|
for query in self._queries.values():
|
456
|
-
query.
|
450
|
+
if query.run_at_startup:
|
451
|
+
query.send(flush=False)
|
457
452
|
self.output.flush()
|
458
453
|
|
459
454
|
def _tiocgwnsz(self) -> tuple[int, int, int, int]:
|
euporie/core/utils.py
CHANGED
@@ -32,12 +32,10 @@ class ChainedList(Sequence[T]):
|
|
32
32
|
return list(chain.from_iterable(self.lists))
|
33
33
|
|
34
34
|
@overload
|
35
|
-
def __getitem__(self, i: int) -> T:
|
36
|
-
...
|
35
|
+
def __getitem__(self, i: int) -> T: ...
|
37
36
|
|
38
37
|
@overload
|
39
|
-
def __getitem__(self, i: slice) -> list[T]:
|
40
|
-
...
|
38
|
+
def __getitem__(self, i: slice) -> list[T]: ...
|
41
39
|
|
42
40
|
def __getitem__(self, i):
|
43
41
|
"""Get an item from the chained lists."""
|