refactor: few more option to save, and some returns are lists.

This commit is contained in:
Mr Finchum 2025-01-31 17:30:01 +01:00
parent e7305f6ecf
commit d08b4b9807

View file

@ -3,23 +3,26 @@ import subprocess
import sys import sys
import os import os
import time import time
import json
from packaging import version from packaging import version
from xml.etree import ElementTree as ET from xml.etree import ElementTree as ET
class PyPiUpdater: class PyPiUpdater:
def __init__(self, package_name, local_version, log_path, update_interval_seconds=20 * 3600): def __init__(self, package_name, local_version, log_path, update_interval_seconds = 20 * 3600):
""" """
Initialize PyPiUpdater. Initialize PyPiUpdater.
:param package_name: Name of the package on PyPI. :param package_name: Name of the package on PyPI.
:param local_version: Currently installed version. :param local_version: Currently installed version.
:param log_path: Path to the update log file (txt file). :param log_path: Path to the update log file (JSON file).
:param update_interval_seconds: Seconds to wait before update is allowed again (default: 20 hours). :param update_interval_seconds: Seconds to wait before update is allowed again (default: 20 hours).
""" """
self.package_name = package_name self.package_name = package_name
self.local_version = version.parse(local_version) self.local_version = version.parse(local_version)
self.log_path = log_path self.log_path = log_path
self.update_interval = update_interval_seconds self.update_interval = update_interval_seconds
self.latest_version = ""
self.last_update_check = 0.1
def _get_latest_version(self): def _get_latest_version(self):
"""Fetch the latest version from PyPI RSS feed.""" """Fetch the latest version from PyPI RSS feed."""
@ -31,6 +34,7 @@ class PyPiUpdater:
root = ET.fromstring(response.content) root = ET.fromstring(response.content)
latest_version = root.find(".//item/title").text.strip() latest_version = root.find(".//item/title").text.strip()
self.latest_version = latest_version
return latest_version, None return latest_version, None
except requests.exceptions.RequestException as e: except requests.exceptions.RequestException as e:
return None, f"Network error: {str(e)}" return None, f"Network error: {str(e)}"
@ -39,7 +43,6 @@ class PyPiUpdater:
def check_for_update(self, force = False): def check_for_update(self, force = False):
"""Check if an update is available.""" """Check if an update is available."""
if not force and not self.should_check_for_update(): if not force and not self.should_check_for_update():
return None, "Checked too recently" return None, "Checked too recently"
@ -49,7 +52,7 @@ class PyPiUpdater:
is_newer = version.parse(latest_version) > self.local_version is_newer = version.parse(latest_version) > self.local_version
if is_newer: if is_newer:
self.record_update_check() # Save the check timestamp only if successful self.record_update_check() # Save check timestamp & latest version
return is_newer, latest_version return is_newer, latest_version
def update_package(self): def update_package(self):
@ -68,31 +71,62 @@ class PyPiUpdater:
subprocess.run([python] + sys.argv) subprocess.run([python] + sys.argv)
sys.exit() sys.exit()
def last_update_check(self): def get_last_state(self):
"""Retrieve the last update check timestamp from a text file.""" """Retrieve last update info for the package."""
if not os.path.exists(self.log_path): data = self._read_json()
return 0 # If no log, assume a long time passed if self.package_name in data:
entry = data[self.package_name]
last_check = self.last_update_date_string(entry["last_checked"])
return [last_check, entry["last_online_version"], self.package_name]
return [None, None, self.package_name]
try: def record_update_check(self):
with open(self.log_path, "r") as f: """Save the last update check time and online version in JSON."""
last_check = float(f.readline().strip()) # Read first line as timestamp data = self._read_json()
return last_check data[self.package_name] = {
except Exception: "last_checked": time.time(),
return 0 # Handle read errors gracefully "last_online_version": self.latest_version
}
self._write_json(data)
def last_update_date_string(self): def remove_package_entry(self, package_name):
time_float = self.last_update_check() """Remove a specific package entry from the log file."""
data = self._read_json()
if package_name in data:
del data[package_name]
self._write_json(data)
return True
return False
def clear_all_entries(self):
"""Clear all update history."""
self._write_json({})
def should_check_for_update(self):
"""Returns True if enough time has passed since the last check."""
data = self._read_json()
last_check = data.get(self.package_name, {}).get("last_checked", 0)
elapsed_time = time.time() - last_check
return elapsed_time >= self.update_interval
def last_update_date_string(self, time_float):
local_time = time.localtime(time_float) local_time = time.localtime(time_float)
time_string = f"{local_time.tm_mday:02d}/{local_time.tm_mon:02d} {local_time.tm_hour:02d}:{local_time.tm_min:02d}" time_string = f"{local_time.tm_mday:02d}/{local_time.tm_mon:02d} {local_time.tm_hour:02d}:{local_time.tm_min:02d}"
return time_string return time_string
def record_update_check(self):
"""Save the current time as the last update check timestamp."""
with open(self.log_path, "w") as f:
f.write(f"{time.time()}")
def should_check_for_update(self): def _read_json(self):
"""Returns True if enough time has passed since the last check.""" """Read JSON log file."""
last_check = self.last_update_check() if not os.path.exists(self.log_path):
elapsed_time = time.time() - last_check return {}
return elapsed_time >= self.update_interval
try:
with open(self.log_path, "r") as f:
return json.load(f)
except (json.JSONDecodeError, FileNotFoundError):
return {}
def _write_json(self, data):
"""Write data to JSON log file."""
with open(self.log_path, "w") as f:
json.dump(data, f, indent=4)