Merge pull request #4505 from awsr/time-fixes

Functionality update to extensions handling
This commit is contained in:
Vladimir Mandic
2025-12-28 15:06:06 +01:00
committed by GitHub
2 changed files with 46 additions and 24 deletions
+28 -4
View File
@@ -1,6 +1,6 @@
from __future__ import annotations
import os
from datetime import datetime
from datetime import datetime, timezone
import git
from modules import shared, errors
from modules.paths import extensions_dir, extensions_builtin_dir
@@ -11,6 +11,30 @@ if not os.path.exists(extensions_dir):
os.makedirs(extensions_dir)
def parse_isotime(time_string: str) -> datetime:
# If Python minimum version is 3.11+, this function can be replaced with datetime.fromisoformat()
trimmed = time_string.rstrip("Z")
if "." in trimmed:
trimmed = trimmed.split(".")[0]
match len(trimmed):
case 16:
return datetime.strptime(trimmed, "%Y-%m-%dT%H:%M").replace(tzinfo=timezone.utc)
case 19:
return datetime.strptime(trimmed, "%Y-%m-%dT%H:%M:%S").replace(tzinfo=timezone.utc)
case _:
raise ValueError(f"Unexpected time string format: '{time_string}'")
def format_dt(d: datetime) -> str:
if d.tzinfo is None:
return d.strftime('%Y-%m-%d %H:%M')
return d.astimezone(timezone.utc).strftime('%Y-%m-%d %H:%M:%S') # Ensure UTC time is shown (just in case)
def ts2utc(timestamp: int) -> datetime:
return datetime.fromtimestamp(timestamp, timezone.utc)
def active():
if shared.opts.disable_all_extensions == "all":
return []
@@ -106,8 +130,8 @@ class Extension:
self.branch = None
self.remote = None
self.have_info_from_repo = False
self.mtime = 0
self.ctime = 0
self.mtime = "2000-01-01T00:00Z"
self.ctime = "2000-01-01T00:00Z"
def read_info(self, force=False):
if self.have_info_from_repo and not force:
@@ -142,7 +166,7 @@ class Extension:
except Exception:
self.branch = 'unknown'
self.commit_hash = head.hexsha
self.version = f"<p>{self.commit_hash[:8]}</p><p>{datetime.fromtimestamp(self.commit_date).strftime('%a %b%d %Y %H:%M')}</p>"
self.version = f"<p>{self.commit_hash[:8]}</p><p>{format_dt(ts2utc(self.commit_date))}</p>"
except Exception as ex:
shared.log.error(f"Extension: failed reading data from git repo={self.name}: {ex}")
self.remote = None
+18 -20
View File
@@ -3,7 +3,7 @@ import json
import shutil
import errno
import html
from datetime import datetime, timedelta
from datetime import datetime, timezone, timedelta
import gradio as gr
from modules import extensions, shared, paths, errors, ui_symbols, call_queue
@@ -37,12 +37,9 @@ def get_installed(ext):
def list_extensions():
global extensions_list # pylint: disable=global-statement
fn = os.path.join(paths.script_path, "html", "extensions.json")
extensions_list = shared.readfile(fn, silent=True) or []
if type(extensions_list) != list:
shared.log.warning(f'Invalid extensions list: file="{fn}"')
extensions_list = []
extensions_list = shared.readfile(fn, silent=True, as_type="list")
if len(extensions_list) == 0:
shared.log.info('Extension list is empty: refresh required')
shared.log.info("Extension List: No information found. Refresh required.")
found = []
for ext in extensions.extensions:
ext.read_info()
@@ -111,10 +108,10 @@ def check_updates(_id_task, disable_list, search_text, sort_column):
ext.git_fetch()
ext.read_info()
commit_date = ext.commit_date or 1577836800
shared.log.info(f'Extensions updated: {ext.name} {ext.commit_hash[:8]} {datetime.utcfromtimestamp(commit_date)}')
shared.log.info(f'Extensions updated: {ext.name} {ext.commit_hash[:8]} {extensions.format_dt(extensions.ts2utc(commit_date))}')
else:
commit_date = ext.commit_date or 1577836800
shared.log.debug(f'Extensions no update available: {ext.name} {ext.commit_hash[:8]} {datetime.utcfromtimestamp(commit_date)}')
shared.log.debug(f'Extensions no update available: {ext.name} {ext.commit_hash[:8]} {extensions.format_dt(extensions.ts2utc(commit_date))}')
except FileNotFoundError as e:
if 'FETCH_HEAD' not in str(e):
raise
@@ -232,10 +229,10 @@ def update_extension(extension_path, search_text, sort_column):
ext.git_fetch()
ext.read_info()
commit_date = ext.commit_date or 1577836800
shared.log.info(f'Extensions updated: {ext.name} {ext.commit_hash[:8]} {datetime.utcfromtimestamp(commit_date)}')
shared.log.info(f'Extensions updated: {ext.name} {ext.commit_hash[:8]} {extensions.format_dt(extensions.ts2utc(commit_date))}')
else:
commit_date = ext.commit_date or 1577836800
shared.log.info(f'Extensions no update available: {ext.name} {ext.commit_hash[:8]} {datetime.utcfromtimestamp(commit_date)}')
shared.log.info(f'Extensions no update available: {ext.name} {ext.commit_hash[:8]} {extensions.format_dt(extensions.ts2utc(commit_date))}')
except FileNotFoundError as e:
if 'FETCH_HEAD' not in str(e):
raise
@@ -308,13 +305,13 @@ def create_html(search_text, sort_column):
ext['enabled'] = installed.enabled if installed is not None else ''
ext['remote'] = installed.remote if installed is not None else None
ext['path'] = installed.path if installed is not None else ''
ext['sort_default'] = f"{'1' if ext['is_builtin'] else '0'}{'1' if ext['installed'] else '0'}{ext.get('updated', '2000-01-01T00:00')}"
ext['sort_default'] = f"{'1' if ext['is_builtin'] else '0'}{'1' if ext['installed'] else '0'}{ext.get('updated', '2000-01-01T00:00Z')}"
sort_reverse, sort_function = sort_ordering[sort_column]
def dt(x: str):
val = ext.get(x, None)
try:
return datetime.fromisoformat(val[:-1]).strftime('%a %b%d %Y %H:%M') if val is not None else "N/A"
return extensions.format_dt(extensions.parse_isotime(val)) if val is not None else "N/A"
except Exception:
return 'N/A'
@@ -322,22 +319,23 @@ def create_html(search_text, sort_column):
for ext in sorted(extensions_list, key=sort_function, reverse=sort_reverse):
installed = get_installed(ext)
author = ''
updated = datetime.timestamp(datetime.now())
updated = datetime.now(timezone.utc) # TZ-aware
try:
if 'github' in ext['url']:
author = 'Author: ' + ext['url'].split('/')[-2].split(':')[-1] if '/' in ext['url'] else ext['url'].split(':')[1].split('/')[0]
updated = datetime.timestamp(datetime.fromisoformat(ext.get('updated', '2000-01-01T00:00:00.000Z').rstrip('Z')))
updated = extensions.parse_isotime(ext.get('updated', '2000-01-01T00:00:00Z')) # TZ-aware
else:
debug(f'Extension not from github: name={ext["name"]} url={ext["url"]}')
except Exception as e:
debug(f'Extension get updated error: name={ext["name"]} url={ext["url"]} {e}')
update_available = (installed is not None) and (ext['remote'] is not None) and (updated > ext['commit_date'])
local_ver_date = extensions.ts2utc(ext['commit_date']) # TZ-aware
update_available = (installed is not None) and (not ext['is_builtin']) and (ext['remote'] is not None) and (updated > local_ver_date) # TZ-aware
if update_available:
debug(f'Extension update available: name={ext["name"]} updated={updated}/{datetime.utcfromtimestamp(updated)} commit={ext["commit_date"]}/{datetime.utcfromtimestamp(ext["commit_date"])}')
debug(f'Extension update available: name={ext["name"]} updated={extensions.format_dt(updated)} commit={extensions.format_dt(local_ver_date)}') # TZ-aware
ext['sort_user'] = f"{'0' if ext['is_builtin'] else '1'}{'1' if ext['installed'] else '0'}{ext.get('name', '')}"
ext['sort_enabled'] = f"{'0' if ext['enabled'] else '1'}{'1' if ext['is_builtin'] else '0'}{'1' if ext['installed'] else '0'}{ext.get('updated', '2000-01-01T00:00')}"
ext['sort_update'] = f"{'1' if update_available else '0'}{'1' if ext['installed'] else '0'}{ext.get('updated', '2000-01-01T00:00')}"
delta = datetime.now() - datetime.fromisoformat(ext.get('created', '2000-01-01T00:00Z')[:-1])
ext['sort_enabled'] = f"{'0' if ext['enabled'] else '1'}{'1' if ext['is_builtin'] else '0'}{'1' if ext['installed'] else '0'}{ext.get('updated', '2000-01-01T00:00Z')}"
ext['sort_update'] = f"{'1' if update_available else '0'}{'1' if ext['installed'] else '0'}{ext.get('updated', '2000-01-01T00:00Z')}"
delta = datetime.now(timezone.utc) - extensions.parse_isotime(ext.get('created', '2000-01-01T00:00Z')) # TZ-aware to prep for 3.11+ datetime.fromisoformat() behavior
ext['sort_trending'] = round(ext.get('stars', 0) / max(delta.days, 5), 1)
tags = ext.get("tags", [])
if not isinstance(tags, list):
@@ -399,7 +397,7 @@ def create_html(search_text, sort_column):
else:
status = f"<div style='cursor:help;width:1em;' title='Unknown status'>{ui_symbols.svg_bullet.color('#008EBC')}</div>"
else:
if updated < datetime.timestamp(datetime.now() - timedelta(6*30)):
if updated < datetime.now(timezone.utc) - timedelta(6*30): # TZ-aware
status = f"<div style='cursor:help;width:1em;' title='Unmaintained'>{ui_symbols.svg_bullet.color('#C000CF')}</div>"
else:
status = f"<div style='cursor:help;width:1em;' title='No info'>{ui_symbols.svg_bullet.color('#7C7C7C')}</div>"