Skip to content

Commit

Permalink
common: general improvements
Browse files Browse the repository at this point in the history
- if timeout happens we try 5 times before
  sending Timeout error to users.

- improve user experience when timeout occurs

- Added console source tree for handling messages

Resolves: #693
Signed-off-by: Douglas Schilling Landgraf <[email protected]>

Signed-off-by: Douglas Schilling Landgraf <[email protected]>
  • Loading branch information
dougsland committed Feb 3, 2025
1 parent 900eca6 commit 9f86dec
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 15 deletions.
2 changes: 1 addition & 1 deletion install.sh
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ setup_ramalama() {
"common.py" "__init__.py" "quadlet.py" "kube.py" \
"oci.py" "version.py" "shortnames.py" "toml_parser.py" \
"file.py" "http_client.py" "url.py" "annotations.py" \
"gpu_detector.py" )
"gpu_detector.py", "console.py" )
for i in "${python_files[@]}"; do
if $local_install; then
url="ramalama/${i}"
Expand Down
74 changes: 60 additions & 14 deletions ramalama/common.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,29 @@
"""ramalama common module."""

import glob
import hashlib
import os
import random
import logging
import shutil
import string
import subprocess
import time
import sys
import urllib.request
import urllib.error
import ramalama.console as console

from ramalama.http_client import HttpClient


logging.basicConfig(
level=logging.WARNING,
format="%(asctime)s - %(levelname)s - %(message)s"
)

MNT_DIR = "/mnt/models"
MNT_FILE = f"{MNT_DIR}/model.file"
HTTP_RANGE_NOT_SATISFIABLE = 416


def container_manager():
Expand Down Expand Up @@ -165,25 +175,61 @@ def download_file(url, dest_path, headers=None, show_progress=True):
headers (dict): Optional headers to include in the request.
show_progress (bool): Whether to show a progress bar during download.
Returns:
None
Raises:
RuntimeError: If the download fails after multiple attempts.
"""
http_client = HttpClient()

headers = headers or {}

# if we are not a tty, don't show progress, can pollute CI output and such
# If not running in a TTY, disable progress to prevent CI pollution
if not sys.stdout.isatty():
show_progress = False

try:
http_client.init(url=url, headers=headers, output_file=dest_path, progress=show_progress)
except urllib.error.HTTPError as e:
if e.code == 416: # Range not satisfiable
if show_progress:
print(f"File {url} already fully downloaded.")
else:
raise e
http_client = HttpClient()
max_retries = 5 # Stop after 5 failures
retries = 0

while retries < max_retries:
try:
# Initialize HTTP client for the request
http_client.init(url=url, headers=headers, output_file=dest_path, progress=show_progress)
return # Exit function if successful

except urllib.error.HTTPError as e:
if e.code == HTTP_RANGE_NOT_SATISFIABLE: # "Range Not Satisfiable" error (file already downloaded)
return # No need to retry

except urllib.error.URLError as e:
console.error(f"Network Error: {e.reason}")
retries += 1

except TimeoutError:
retries += 1
console.warning(f"TimeoutError: The server took too long to respond. Retrying {retries}/{max_retries}...")

except RuntimeError as e: # Catch network-related errors from HttpClient
retries += 1
console.warning(f"{e}. Retrying {retries}/{max_retries}...")

except IOError as e:
retries += 1
console.warning(f"I/O Error: {e}. Retrying {retries}/{max_retries}...")

except Exception as e:
console.error(f"Unexpected error: {str(e)}")
raise

if retries >= max_retries:
error_message = (
"\nDownload failed after multiple attempts.\n"
"Possible causes:\n"
"- Internet connection issue\n"
"- Server is down or unresponsive\n"
"- Firewall or proxy blocking the request\n"
)
console.error(error_message)
sys.exit(1)

time.sleep(2 ** retries * 0.1) # Exponential backoff (0.1s, 0.2s, 0.4s...)


def engine_version(engine):
Expand Down
35 changes: 35 additions & 0 deletions ramalama/console.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import os
import sys
import locale
import logging

def is_locale_utf8():
"""Check if the system locale is UTF-8."""
return 'UTF-8' in os.getenv('LC_CTYPE', '') or 'UTF-8' in os.getenv('LANG', '')

def supports_emoji():
"""Detect if the terminal supports emoji output."""
term = os.getenv("TERM")
if not term or term in ("dumb", "linux"):
return False

return is_locale_utf8()

# Allow users to override emoji support via an environment variable
# If RAMALAMA_FORCE_EMOJI is not set, it defaults to checking supports_emoji()
RAMALAMA_FORCE_EMOJI = os.getenv("RAMALAMA_FORCE_EMOJI")
FORCE_EMOJI = RAMALAMA_FORCE_EMOJI.lower() == "true" if RAMALAMA_FORCE_EMOJI else None
EMOJI = FORCE_EMOJI if FORCE_EMOJI is not None else supports_emoji()

# Define emoji-aware logging messages
def error(msg):
formatted_msg = f"❌ {msg}" if EMOJI else f"[ERROR] {msg}"
logging.error(formatted_msg)

def warning(msg):
formatted_msg = f"⚠️ {msg}" if EMOJI else f"[WARNING] {msg}"
logging.warning(formatted_msg)

def info(msg):
formatted_msg = f"ℹ️ {msg}" if EMOJI else f"[INFO] {msg}"
logging.info(formatted_msg)

0 comments on commit 9f86dec

Please sign in to comment.