Merge pull request #10980 from AUTOMATIC1111/sysinfo
Added sysinfo tab to settings
This commit is contained in:
commit
efc4c79b5e
@ -3,10 +3,30 @@ import textwrap
|
||||
import traceback
|
||||
|
||||
|
||||
exception_records = []
|
||||
|
||||
|
||||
def record_exception():
|
||||
_, e, tb = sys.exc_info()
|
||||
if e is None:
|
||||
return
|
||||
|
||||
if exception_records and exception_records[-1] == e:
|
||||
return
|
||||
|
||||
exception_records.append((e, tb))
|
||||
|
||||
if len(exception_records) > 5:
|
||||
exception_records.pop(0)
|
||||
|
||||
|
||||
def report(message: str, *, exc_info: bool = False) -> None:
|
||||
"""
|
||||
Print an error message to stderr, with optional traceback.
|
||||
"""
|
||||
|
||||
record_exception()
|
||||
|
||||
for line in message.splitlines():
|
||||
print("***", line, file=sys.stderr)
|
||||
if exc_info:
|
||||
@ -15,6 +35,8 @@ def report(message: str, *, exc_info: bool = False) -> None:
|
||||
|
||||
|
||||
def print_error_explanation(message):
|
||||
record_exception()
|
||||
|
||||
lines = message.strip().split("\n")
|
||||
max_len = max([len(x) for x in lines])
|
||||
|
||||
@ -25,6 +47,8 @@ def print_error_explanation(message):
|
||||
|
||||
|
||||
def display(e: Exception, task, *, full_traceback=False):
|
||||
record_exception()
|
||||
|
||||
print(f"{task or 'error'}: {type(e).__name__}", file=sys.stderr)
|
||||
te = traceback.TracebackException.from_exception(e)
|
||||
if full_traceback:
|
||||
@ -44,6 +68,8 @@ already_displayed = {}
|
||||
|
||||
|
||||
def display_once(e: Exception, task):
|
||||
record_exception()
|
||||
|
||||
if task in already_displayed:
|
||||
return
|
||||
|
||||
|
162
modules/sysinfo.py
Normal file
162
modules/sysinfo.py
Normal file
@ -0,0 +1,162 @@
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
import platform
|
||||
import hashlib
|
||||
import pkg_resources
|
||||
import psutil
|
||||
import re
|
||||
|
||||
import launch
|
||||
from modules import paths_internal, timer
|
||||
|
||||
checksum_token = "DontStealMyGamePlz__WINNERS_DONT_USE_DRUGS__DONT_COPY_THAT_FLOPPY"
|
||||
environment_whitelist = {
|
||||
"GIT",
|
||||
"INDEX_URL",
|
||||
"WEBUI_LAUNCH_LIVE_OUTPUT",
|
||||
"GRADIO_ANALYTICS_ENABLED",
|
||||
"PYTHONPATH",
|
||||
"TORCH_INDEX_URL",
|
||||
"TORCH_COMMAND",
|
||||
"REQS_FILE",
|
||||
"XFORMERS_PACKAGE",
|
||||
"GFPGAN_PACKAGE",
|
||||
"CLIP_PACKAGE",
|
||||
"OPENCLIP_PACKAGE",
|
||||
"STABLE_DIFFUSION_REPO",
|
||||
"K_DIFFUSION_REPO",
|
||||
"CODEFORMER_REPO",
|
||||
"BLIP_REPO",
|
||||
"STABLE_DIFFUSION_COMMIT_HASH",
|
||||
"K_DIFFUSION_COMMIT_HASH",
|
||||
"CODEFORMER_COMMIT_HASH",
|
||||
"BLIP_COMMIT_HASH",
|
||||
"COMMANDLINE_ARGS",
|
||||
"IGNORE_CMD_ARGS_ERRORS",
|
||||
}
|
||||
|
||||
|
||||
def pretty_bytes(num, suffix="B"):
|
||||
for unit in ["", "K", "M", "G", "T", "P", "E", "Z", "Y"]:
|
||||
if abs(num) < 1024 or unit == 'Y':
|
||||
return f"{num:.0f}{unit}{suffix}"
|
||||
num /= 1024
|
||||
|
||||
|
||||
def get():
|
||||
res = get_dict()
|
||||
|
||||
text = json.dumps(res, ensure_ascii=False, indent=4)
|
||||
|
||||
h = hashlib.sha256(text.encode("utf8"))
|
||||
text = text.replace(checksum_token, h.hexdigest())
|
||||
|
||||
return text
|
||||
|
||||
|
||||
re_checksum = re.compile(r'"Checksum": "([0-9a-fA-F]{64})"')
|
||||
|
||||
|
||||
def check(x):
|
||||
m = re.search(re_checksum, x)
|
||||
if not m:
|
||||
return False
|
||||
|
||||
replaced = re.sub(re_checksum, f'"Checksum": "{checksum_token}"', x)
|
||||
|
||||
h = hashlib.sha256(replaced.encode("utf8"))
|
||||
return h.hexdigest() == m.group(1)
|
||||
|
||||
|
||||
def get_dict():
|
||||
ram = psutil.virtual_memory()
|
||||
|
||||
res = {
|
||||
"Platform": platform.platform(),
|
||||
"Python": platform.python_version(),
|
||||
"Version": launch.git_tag(),
|
||||
"Commit": launch.commit_hash(),
|
||||
"Script path": paths_internal.script_path,
|
||||
"Data path": paths_internal.data_path,
|
||||
"Extensions dir": paths_internal.extensions_dir,
|
||||
"Checksum": checksum_token,
|
||||
"Commandline": sys.argv,
|
||||
"Torch env info": get_torch_sysinfo(),
|
||||
"Exceptions": get_exceptions(),
|
||||
"CPU": {
|
||||
"model": platform.processor(),
|
||||
"count logical": psutil.cpu_count(logical=True),
|
||||
"count physical": psutil.cpu_count(logical=False),
|
||||
},
|
||||
"RAM": {
|
||||
x: pretty_bytes(getattr(ram, x, 0)) for x in ["total", "used", "free", "active", "inactive", "buffers", "cached", "shared"] if getattr(ram, x, 0) != 0
|
||||
},
|
||||
"Extensions": get_extensions(enabled=True),
|
||||
"Inactive extensions": get_extensions(enabled=False),
|
||||
"Environment": get_environment(),
|
||||
"Config": get_config(),
|
||||
"Startup": timer.startup_record,
|
||||
"Packages": sorted([f"{pkg.key}=={pkg.version}" for pkg in pkg_resources.working_set]),
|
||||
}
|
||||
|
||||
return res
|
||||
|
||||
|
||||
def format_traceback(tb):
|
||||
return [[f"{x.filename}, line {x.lineno}, {x.name}", x.line] for x in traceback.extract_tb(tb)]
|
||||
|
||||
|
||||
def get_exceptions():
|
||||
try:
|
||||
from modules import errors
|
||||
|
||||
return [{"exception": str(e), "traceback": format_traceback(tb)} for e, tb in reversed(errors.exception_records)]
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
|
||||
|
||||
def get_environment():
|
||||
return {k: os.environ[k] for k in sorted(os.environ) if k in environment_whitelist}
|
||||
|
||||
|
||||
re_newline = re.compile(r"\r*\n")
|
||||
|
||||
|
||||
def get_torch_sysinfo():
|
||||
try:
|
||||
import torch.utils.collect_env
|
||||
info = torch.utils.collect_env.get_env_info()._asdict()
|
||||
|
||||
return {k: re.split(re_newline, str(v)) if "\n" in str(v) else v for k, v in info.items()}
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
|
||||
|
||||
def get_extensions(*, enabled):
|
||||
|
||||
try:
|
||||
from modules import extensions
|
||||
|
||||
def to_json(x: extensions.Extension):
|
||||
return {
|
||||
"name": x.name,
|
||||
"path": x.path,
|
||||
"version": x.version,
|
||||
"branch": x.branch,
|
||||
"remote": x.remote,
|
||||
}
|
||||
|
||||
return [to_json(x) for x in extensions.extensions if not x.is_builtin and x.enabled == enabled]
|
||||
except Exception as e:
|
||||
return str(e)
|
||||
|
||||
|
||||
def get_config():
|
||||
try:
|
||||
from modules import shared
|
||||
return shared.opts.data
|
||||
except Exception as e:
|
||||
return str(e)
|
@ -1,3 +1,4 @@
|
||||
import datetime
|
||||
import json
|
||||
import mimetypes
|
||||
import os
|
||||
@ -11,7 +12,7 @@ import numpy as np
|
||||
from PIL import Image, PngImagePlugin # noqa: F401
|
||||
from modules.call_queue import wrap_gradio_gpu_call, wrap_queued_call, wrap_gradio_call
|
||||
|
||||
from modules import sd_hijack, sd_models, script_callbacks, ui_extensions, deepbooru, sd_vae, extra_networks, ui_common, ui_postprocessing, progress, ui_loadsave, errors, shared_items, ui_settings, timer
|
||||
from modules import sd_hijack, sd_models, script_callbacks, ui_extensions, deepbooru, sd_vae, extra_networks, ui_common, ui_postprocessing, progress, ui_loadsave, errors, shared_items, ui_settings, timer, sysinfo
|
||||
from modules.ui_components import FormRow, FormGroup, ToolButton, FormHTML
|
||||
from modules.paths import script_path
|
||||
from modules.ui_common import create_refresh_button
|
||||
@ -1600,3 +1601,15 @@ def setup_ui_api(app):
|
||||
app.add_api_route("/internal/ping", lambda: {}, methods=["GET"])
|
||||
|
||||
app.add_api_route("/internal/profile-startup", lambda: timer.startup_record, methods=["GET"])
|
||||
|
||||
def download_sysinfo(attachment=False):
|
||||
from fastapi.responses import PlainTextResponse
|
||||
|
||||
text = sysinfo.get()
|
||||
filename = f"sysinfo-{datetime.datetime.utcnow().strftime('%Y-%m-%d-%H-%M')}.txt"
|
||||
|
||||
return PlainTextResponse(text, headers={'Content-Disposition': f'{"attachment" if attachment else "inline"}; filename="{filename}"'})
|
||||
|
||||
app.add_api_route("/internal/sysinfo", download_sysinfo, methods=["GET"])
|
||||
app.add_api_route("/internal/sysinfo-download", lambda: download_sysinfo(attachment=True), methods=["GET"])
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
import gradio as gr
|
||||
|
||||
from modules import ui_common, shared, script_callbacks, scripts, sd_models
|
||||
from modules import ui_common, shared, script_callbacks, scripts, sd_models, sysinfo
|
||||
from modules.call_queue import wrap_gradio_call
|
||||
from modules.shared import opts
|
||||
from modules.ui_components import FormRow
|
||||
@ -157,6 +157,17 @@ class UiSettings:
|
||||
with gr.TabItem("Defaults", id="defaults", elem_id="settings_tab_defaults"):
|
||||
loadsave.create_ui()
|
||||
|
||||
with gr.TabItem("Sysinfo", id="sysinfo", elem_id="settings_tab_sysinfo"):
|
||||
gr.HTML('<a href="./internal/sysinfo-download" class="sysinfo_big_link" download>Download system info</a><br /><a href="./internal/sysinfo">(or open as text in a new page)</a>', elem_id="sysinfo_download")
|
||||
|
||||
with gr.Row():
|
||||
with gr.Column(scale=1):
|
||||
sysinfo_check_file = gr.File(label="Check system info for validity", type='binary')
|
||||
with gr.Column(scale=1):
|
||||
sysinfo_check_output = gr.HTML("", elem_id="sysinfo_validity")
|
||||
with gr.Column(scale=100):
|
||||
pass
|
||||
|
||||
with gr.TabItem("Actions", id="actions", elem_id="settings_tab_actions"):
|
||||
request_notifications = gr.Button(value='Request browser notifications', elem_id="request_notifications")
|
||||
download_localization = gr.Button(value='Download localization template', elem_id="download_localization")
|
||||
@ -215,6 +226,21 @@ class UiSettings:
|
||||
outputs=[],
|
||||
)
|
||||
|
||||
def check_file(x):
|
||||
if x is None:
|
||||
return ''
|
||||
|
||||
if sysinfo.check(x.decode('utf8', errors='ignore')):
|
||||
return 'Valid'
|
||||
|
||||
return 'Invalid'
|
||||
|
||||
sysinfo_check_file.change(
|
||||
fn=check_file,
|
||||
inputs=[sysinfo_check_file],
|
||||
outputs=[sysinfo_check_output],
|
||||
)
|
||||
|
||||
self.interface = settings_interface
|
||||
|
||||
def add_quicksettings(self):
|
||||
|
Loading…
Reference in New Issue
Block a user