aws-ssh-utils 0.3.2__tar.gz → 0.3.3__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.
Files changed (22) hide show
  1. {aws_ssh_utils-0.3.2 → aws_ssh_utils-0.3.3}/PKG-INFO +1 -1
  2. aws_ssh_utils-0.3.3/aws_ssh_utils/__init__.py +1 -0
  3. {aws_ssh_utils-0.3.2 → aws_ssh_utils-0.3.3}/aws_ssh_utils/commands/app.py +3 -5
  4. {aws_ssh_utils-0.3.2 → aws_ssh_utils-0.3.3}/aws_ssh_utils/terminal.py +60 -5
  5. aws_ssh_utils-0.3.2/aws_ssh_utils/__init__.py +0 -1
  6. {aws_ssh_utils-0.3.2 → aws_ssh_utils-0.3.3}/.gitignore +0 -0
  7. {aws_ssh_utils-0.3.2 → aws_ssh_utils-0.3.3}/LICENSE +0 -0
  8. {aws_ssh_utils-0.3.2 → aws_ssh_utils-0.3.3}/README.md +0 -0
  9. {aws_ssh_utils-0.3.2 → aws_ssh_utils-0.3.3}/aws_ssh_utils/cli_utils.py +0 -0
  10. {aws_ssh_utils-0.3.2 → aws_ssh_utils-0.3.3}/aws_ssh_utils/commands/__init__.py +0 -0
  11. {aws_ssh_utils-0.3.2 → aws_ssh_utils-0.3.3}/aws_ssh_utils/commands/ec2.py +0 -0
  12. {aws_ssh_utils-0.3.2 → aws_ssh_utils-0.3.3}/aws_ssh_utils/commands/emr.py +0 -0
  13. {aws_ssh_utils-0.3.2 → aws_ssh_utils-0.3.3}/aws_ssh_utils/commands/emr_all.py +0 -0
  14. {aws_ssh_utils-0.3.2 → aws_ssh_utils-0.3.3}/aws_ssh_utils/commands/healthcheck.py +0 -0
  15. {aws_ssh_utils-0.3.2 → aws_ssh_utils-0.3.3}/aws_ssh_utils/connection.py +0 -0
  16. {aws_ssh_utils-0.3.2 → aws_ssh_utils-0.3.3}/aws_ssh_utils/ec2_utils.py +0 -0
  17. {aws_ssh_utils-0.3.2 → aws_ssh_utils-0.3.3}/aws_ssh_utils/emr_utils.py +0 -0
  18. {aws_ssh_utils-0.3.2 → aws_ssh_utils-0.3.3}/aws_ssh_utils/logging_utils.py +0 -0
  19. {aws_ssh_utils-0.3.2 → aws_ssh_utils-0.3.3}/aws_ssh_utils/py.typed +0 -0
  20. {aws_ssh_utils-0.3.2 → aws_ssh_utils-0.3.3}/aws_ssh_utils/session.py +0 -0
  21. {aws_ssh_utils-0.3.2 → aws_ssh_utils-0.3.3}/aws_ssh_utils/ssh.py +0 -0
  22. {aws_ssh_utils-0.3.2 → aws_ssh_utils-0.3.3}/pyproject.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: aws-ssh-utils
3
- Version: 0.3.2
3
+ Version: 0.3.3
4
4
  Summary: Easy AWS SSHing
5
5
  Project-URL: Homepage, https://github.com/mvanderlee/aws-ssh-utils
6
6
  Author-email: Michiel Vanderlee <jmt.vanderlee@gmail.com>
@@ -0,0 +1 @@
1
+ __version__ = "0.3.3"
@@ -25,8 +25,8 @@ from aws_ssh_utils.cli_utils import detect_environment, detect_scrollback, rende
25
25
  from aws_ssh_utils.connection import Attempt, Environment, Target, connect, open_shell_channel
26
26
  from aws_ssh_utils.ec2_utils import ec2_name, ec2_target, get_running_ec2_instances
27
27
  from aws_ssh_utils.emr_utils import emr_target, get_emr_clusters, get_emr_instances, group_role, group_sort_key
28
- from aws_ssh_utils.logging_utils import LoggedApp, terminal_logging
29
- from aws_ssh_utils.terminal import ShellStatus, Terminal
28
+ from aws_ssh_utils.logging_utils import terminal_logging
29
+ from aws_ssh_utils.terminal import ShellStatus, Terminal, TerminalApp
30
30
 
31
31
  if TYPE_CHECKING:
32
32
  from mypy_boto3_ec2 import EC2Client
@@ -165,7 +165,7 @@ class ShellPane(TabPane):
165
165
  self.query_ancestor(TabbedContent).remove_pane(self.id or '')
166
166
 
167
167
 
168
- class SSHApp(LoggedApp):
168
+ class SSHApp(TerminalApp):
169
169
  """The textual app"""
170
170
 
171
171
  TITLE = "AWS SSH"
@@ -183,8 +183,6 @@ class SSHApp(LoggedApp):
183
183
  }
184
184
  .error { color: $error; }
185
185
  """
186
- # Its priority ctrl+p binding would steal shell history navigation from terminals.
187
- ENABLE_COMMAND_PALETTE = False
188
186
  BINDINGS: ClassVar[list[BindingType]] = [
189
187
  Binding('ctrl+q', 'quit', 'Quit', priority=True),
190
188
  Binding('f12', 'toggle_sidebar', 'Toggle sidebar', priority=True),
@@ -1,10 +1,12 @@
1
1
  """Textual widgets rendering SSH and SSM shell channels through the pyte VT100 emulator."""
2
2
 
3
+ import asyncio
3
4
  import datetime as dt
4
5
  import time
5
6
  from collections import deque
6
7
  from collections.abc import Sequence
7
8
  from functools import lru_cache
9
+ from importlib.metadata import version
8
10
  from typing import Any
9
11
 
10
12
  import pyte
@@ -228,17 +230,45 @@ class Terminal(Widget, can_focus=True):
228
230
  event.prevent_default()
229
231
  self.send(data)
230
232
 
231
- def on_paste(self, event: events.Paste):
233
+ async def on_paste(self, event: events.Paste):
234
+ event.stop()
232
235
  text = event.text
236
+ started = time.monotonic()
237
+ logger.bind(file_only=True).info(
238
+ 'Paste shell received: chars={}; bracketed={}',
239
+ len(text),
240
+ BRACKETED_PASTE in self.vt.mode,
241
+ )
233
242
  if BRACKETED_PASTE in self.vt.mode:
234
243
  text = f'\x1b[200~{text}\x1b[201~'
235
- self.send(text)
244
+ if self.scrolled:
245
+ self.scroll_history(-self.scrolled)
246
+ try:
247
+ # SSH flow control may block a large paste. Keep the app loop free
248
+ # so the reader can deliver output while input is still being sent.
249
+ await asyncio.to_thread(self._send_all, text.encode())
250
+ logger.bind(file_only=True).info(
251
+ 'Paste shell sent: bytes={}; elapsed={:.3f}s',
252
+ len(text.encode()),
253
+ time.monotonic() - started,
254
+ )
255
+ except OSError:
256
+ logger.exception('Terminal paste failed')
257
+ self.post_message(self.Closed())
236
258
 
237
259
  def send(self, text: str):
238
260
  """Send user input, jumping back to the live screen like other terminals do."""
239
261
  if self.scrolled:
240
262
  self.scroll_history(-self.scrolled)
241
- self.channel.send(text.encode())
263
+ self._send_all(text.encode())
264
+
265
+ def _send_all(self, data: bytes):
266
+ """A channel write may accept only one SSH packet or the available window."""
267
+ while data:
268
+ sent = self.channel.send(data)
269
+ if sent <= 0:
270
+ raise OSError('Terminal channel closed while sending input')
271
+ data = data[sent:]
242
272
 
243
273
  @override
244
274
  def render_line(self, y: int) -> Strip:
@@ -301,12 +331,37 @@ class ShellStatus(Horizontal):
301
331
  self.query_one('.duration', Static).update(f"{hours:02}:{minutes:02}:{seconds:02}")
302
332
 
303
333
 
304
- class ShellApp(LoggedApp):
305
- """A single full-screen shell, exits when the shell closes."""
334
+ class TerminalApp(LoggedApp):
335
+ """Shared input handling for apps containing interactive shell terminals."""
306
336
 
307
337
  # Its priority ctrl+p binding would steal shell history navigation from the terminal.
308
338
  ENABLE_COMMAND_PALETTE = False
309
339
 
340
+ def on_mount(self):
341
+ logger.bind(file_only=True).info('Terminal input diagnostics v1; textual={}', version('textual'))
342
+
343
+ @override
344
+ async def on_event(self, event: events.Event) -> None:
345
+ if isinstance(event, (events.AppFocus, events.AppBlur)):
346
+ logger.bind(file_only=True).info('Terminal focus event: {}', type(event).__name__)
347
+ if isinstance(event, events.Paste) and not event.is_forwarded and not self.app_focus:
348
+ # A terminal's paste confirmation dialog can send the paste before
349
+ # FocusIn. Textual restores the previous widget for keys/mouse, but
350
+ # not pastes, so they otherwise go to an unfocused screen and vanish.
351
+ self.app_focus = True
352
+ if isinstance(event, events.Paste) and not event.is_forwarded:
353
+ logger.bind(file_only=True).info(
354
+ 'Paste app received: chars={}; app_focus={}; target={}',
355
+ len(event.text),
356
+ self.app_focus,
357
+ type(self.focused).__name__,
358
+ )
359
+ await super().on_event(event)
360
+
361
+
362
+ class ShellApp(TerminalApp):
363
+ """A single full-screen shell, exits when the shell closes."""
364
+
310
365
  def __init__(self, via: str, client: ShellClient, channel: ShellChannel, scrollback: int = 0):
311
366
  super().__init__()
312
367
  self.via = via
@@ -1 +0,0 @@
1
- __version__ = "0.3.2"
File without changes
File without changes
File without changes