hte-cli 0.2.7__py3-none-any.whl → 0.2.9__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.
hte_cli/cli.py CHANGED
@@ -323,22 +323,25 @@ def session_join(ctx, session_id: str, force_setup: bool):
323
323
  parts = line.split(": ", 1)
324
324
  if len(parts) == 2:
325
325
  layer_id = parts[0][-8:] # Last 8 chars of layer ID
326
- layer_status = parts[1][:40]
327
- # Show progress bars for Downloading/Extracting
326
+ layer_status = parts[1] # Don't truncate - keep full progress
327
+ # Show progress bars for Downloading/Extracting with MB info
328
328
  if "Downloading" in layer_status or "Extracting" in layer_status:
329
- display_text = f"{layer_id}: {layer_status}"
329
+ # Keep progress: "[====> ] 10.5MB/50MB"
330
+ display_text = f"{layer_id}: {layer_status[:50]}"
330
331
  elif "Pull complete" in layer_status:
331
332
  display_text = f"{layer_id}: done"
332
333
  elif "Download complete" in layer_status:
333
- display_text = f"{layer_id}: downloaded, extracting..."
334
+ display_text = f"{layer_id}: download done"
334
335
  elif "Already exists" in layer_status:
335
336
  display_text = f"{layer_id}: cached"
336
337
  elif "Waiting" in layer_status:
337
- display_text = f"{layer_id}: waiting..."
338
+ display_text = f"{layer_id}: waiting"
339
+ elif "Verifying" in layer_status:
340
+ display_text = f"{layer_id}: verifying"
338
341
  else:
339
- display_text = layer_status[:50]
342
+ display_text = line[:55]
340
343
  elif line.strip():
341
- display_text = line[:50]
344
+ display_text = line[:55]
342
345
 
343
346
  if display_text and display_text != last_status[0]:
344
347
  last_status[0] = display_text
hte_cli/image_utils.py CHANGED
@@ -1,6 +1,10 @@
1
1
  """Docker image utilities for pre-pulling compose images."""
2
2
 
3
3
  import logging
4
+ import os
5
+ import pty
6
+ import re
7
+ import select
4
8
  import subprocess
5
9
  from collections.abc import Callable
6
10
 
@@ -61,32 +65,77 @@ def pull_image_with_progress(
61
65
  on_complete: Callable[[str, bool], None] | None = None,
62
66
  ) -> bool:
63
67
  """
64
- Pull a Docker image with progress callbacks.
68
+ Pull a Docker image with progress callbacks using PTY for real progress output.
65
69
 
66
70
  Args:
67
71
  image: Image name to pull
68
- on_progress: Callback(image, status_line) called for each line of output
72
+ on_progress: Callback(image, status_line) called for each progress update
69
73
  on_complete: Callback(image, success) called when pull completes
70
74
 
71
75
  Returns:
72
76
  True if pull succeeded, False otherwise
73
77
  """
74
78
  try:
79
+ # Use PTY to get real progress output from docker
80
+ master_fd, slave_fd = pty.openpty()
81
+
75
82
  process = subprocess.Popen(
76
83
  ["docker", "pull", image],
77
- stdout=subprocess.PIPE,
78
- stderr=subprocess.STDOUT,
79
- text=True,
80
- bufsize=1,
84
+ stdout=slave_fd,
85
+ stderr=slave_fd,
86
+ stdin=slave_fd,
87
+ close_fds=True,
81
88
  )
82
89
 
83
- # Stream output line by line
84
- for line in iter(process.stdout.readline, ""):
85
- line = line.strip()
86
- if line and on_progress:
87
- on_progress(image, line)
90
+ os.close(slave_fd) # Close slave in parent
91
+
92
+ # Read output from master with timeout
93
+ output_buffer = ""
94
+ # Regex to parse docker progress: "abc123: Downloading [===> ] 10.5MB/50MB"
95
+ progress_pattern = re.compile(
96
+ r"([a-f0-9]+):\s*(Downloading|Extracting|Verifying Checksum|Download complete|Pull complete|Already exists|Waiting)(?:\s+\[.*?\]\s+)?(\d+\.?\d*\s*[kMG]?B)?(?:/(\d+\.?\d*\s*[kMG]?B))?"
97
+ )
88
98
 
89
- process.wait()
99
+ while True:
100
+ # Check if process is done
101
+ ret = process.poll()
102
+ if ret is not None:
103
+ # Read any remaining output
104
+ try:
105
+ while True:
106
+ ready, _, _ = select.select([master_fd], [], [], 0.1)
107
+ if not ready:
108
+ break
109
+ chunk = os.read(master_fd, 4096)
110
+ if not chunk:
111
+ break
112
+ except OSError:
113
+ pass
114
+ break
115
+
116
+ # Read available output
117
+ try:
118
+ ready, _, _ = select.select([master_fd], [], [], 0.1)
119
+ if ready:
120
+ chunk = os.read(master_fd, 4096)
121
+ if chunk:
122
+ output_buffer += chunk.decode("utf-8", errors="replace")
123
+
124
+ # Parse and report progress
125
+ # Docker uses carriage returns to update lines in place
126
+ lines = output_buffer.replace("\r", "\n").split("\n")
127
+ output_buffer = lines[-1] # Keep incomplete line
128
+
129
+ for line in lines[:-1]:
130
+ line = line.strip()
131
+ # Strip ANSI escape codes
132
+ line = re.sub(r"\x1b\[[0-9;]*[a-zA-Z]", "", line)
133
+ if line and on_progress:
134
+ on_progress(image, line)
135
+ except OSError:
136
+ break
137
+
138
+ os.close(master_fd)
90
139
  success = process.returncode == 0
91
140
 
92
141
  if on_complete:
@@ -94,7 +143,7 @@ def pull_image_with_progress(
94
143
 
95
144
  return success
96
145
 
97
- except (subprocess.TimeoutExpired, FileNotFoundError, OSError) as e:
146
+ except (FileNotFoundError, OSError) as e:
98
147
  logger.error(f"Failed to pull {image}: {e}")
99
148
  if on_complete:
100
149
  on_complete(image, False)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: hte-cli
3
- Version: 0.2.7
3
+ Version: 0.2.9
4
4
  Summary: Human Time-to-Completion Evaluation CLI
5
5
  Project-URL: Homepage, https://github.com/sean-peters-au/lyptus-mono
6
6
  Author: Lyptus Research
@@ -1,15 +1,15 @@
1
1
  hte_cli/__init__.py,sha256=fDGXp-r8bIoLtlQnn5xJ_CpwMhonvk9bGjZQsjA2mDI,914
2
2
  hte_cli/__main__.py,sha256=63n0gNGfskidWDU0aAIF2N8lylVCLYKVIkrN9QiORoo,107
3
3
  hte_cli/api_client.py,sha256=m42kfFZS72Nu_VuDwxRsLNy4ziCcvgk7KNWBh9gwqy0,9257
4
- hte_cli/cli.py,sha256=OBRo5QREEedf8CVzwvm7fecaoLc3jJPVbQgN805HjH0,42496
4
+ hte_cli/cli.py,sha256=W9R_jHBLhLho2GyroKzCCg6EhBluCrFJdZ9zCaKFGuo,42745
5
5
  hte_cli/config.py,sha256=42Xv__YMSeRLs2zhGukJkIXFKtnBtYCHnONfViGyt2g,3387
6
6
  hte_cli/errors.py,sha256=1J5PpxcUKBu6XjigMMCPOq4Zc12tnv8LhAsiaVFWLQM,2762
7
7
  hte_cli/events.py,sha256=Zn-mroqaLHNzdT4DFf8st1Qclglshihdc09dBfCN070,5522
8
- hte_cli/image_utils.py,sha256=454yoZEI1duNYrZC8UjhfZzDRP4Nxdrf2TvnZ_54G1k,4439
8
+ hte_cli/image_utils.py,sha256=TLwJdswUQrSD2bQcAXW03R8j8WG2pbHzd12TWcE7zy4,6418
9
9
  hte_cli/runner.py,sha256=DhC8FMjHwfLR193iP4thLDRZrNssYA9KH1WYKU2JKeg,13535
10
10
  hte_cli/scorers.py,sha256=sFoPJePRt-K191-Ga4cVmrldruJclYXTOLkU_C9nCDI,6025
11
11
  hte_cli/version_check.py,sha256=WVZyGy2XfAghQYdd2N9-0Qfg-7pgp9gt4761-PnmacI,1708
12
- hte_cli-0.2.7.dist-info/METADATA,sha256=TZEGl2C0lrQoYqd5GonMmqbyD4T91Te1iKJArOikhZs,3767
13
- hte_cli-0.2.7.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
14
- hte_cli-0.2.7.dist-info/entry_points.txt,sha256=XbyEEi1H14DFAt0Kdl22e_IRVEGzimSzYSh5HlhKlFA,41
15
- hte_cli-0.2.7.dist-info/RECORD,,
12
+ hte_cli-0.2.9.dist-info/METADATA,sha256=CoIpqVDlDBi7xzh6hp6iNd2U6aj5W6R9Z6oaJPz33FM,3767
13
+ hte_cli-0.2.9.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
14
+ hte_cli-0.2.9.dist-info/entry_points.txt,sha256=XbyEEi1H14DFAt0Kdl22e_IRVEGzimSzYSh5HlhKlFA,41
15
+ hte_cli-0.2.9.dist-info/RECORD,,