Unverified Commit efc4c79b authored by AUTOMATIC1111's avatar AUTOMATIC1111 Committed by GitHub
Browse files

Merge pull request #10980 from AUTOMATIC1111/sysinfo

Added sysinfo tab to settings
parents b4b7e6e5 aeba3cad
Loading
Loading
Loading
Loading
+26 −0
Original line number Original line Diff line number Diff line
@@ -3,10 +3,30 @@ import textwrap
import traceback
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:
def report(message: str, *, exc_info: bool = False) -> None:
    """
    """
    Print an error message to stderr, with optional traceback.
    Print an error message to stderr, with optional traceback.
    """
    """

    record_exception()

    for line in message.splitlines():
    for line in message.splitlines():
        print("***", line, file=sys.stderr)
        print("***", line, file=sys.stderr)
    if exc_info:
    if exc_info:
@@ -15,6 +35,8 @@ def report(message: str, *, exc_info: bool = False) -> None:




def print_error_explanation(message):
def print_error_explanation(message):
    record_exception()

    lines = message.strip().split("\n")
    lines = message.strip().split("\n")
    max_len = max([len(x) for x in lines])
    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):
def display(e: Exception, task, *, full_traceback=False):
    record_exception()

    print(f"{task or 'error'}: {type(e).__name__}", file=sys.stderr)
    print(f"{task or 'error'}: {type(e).__name__}", file=sys.stderr)
    te = traceback.TracebackException.from_exception(e)
    te = traceback.TracebackException.from_exception(e)
    if full_traceback:
    if full_traceback:
@@ -44,6 +68,8 @@ already_displayed = {}




def display_once(e: Exception, task):
def display_once(e: Exception, task):
    record_exception()

    if task in already_displayed:
    if task in already_displayed:
        return
        return


modules/sysinfo.py

0 → 100644
+162 −0
Original line number Original line Diff line number Diff line
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)
+14 −1
Original line number Original line Diff line number Diff line
import datetime
import json
import json
import mimetypes
import mimetypes
import os
import os
@@ -11,7 +12,7 @@ import numpy as np
from PIL import Image, PngImagePlugin  # noqa: F401
from PIL import Image, PngImagePlugin  # noqa: F401
from modules.call_queue import wrap_gradio_gpu_call, wrap_queued_call, wrap_gradio_call
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.ui_components import FormRow, FormGroup, ToolButton, FormHTML
from modules.paths import script_path
from modules.paths import script_path
from modules.ui_common import create_refresh_button
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/ping", lambda: {}, methods=["GET"])


    app.add_api_route("/internal/profile-startup", lambda: timer.startup_record, 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"])
+27 −1
Original line number Original line Diff line number Diff line
import gradio as gr
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.call_queue import wrap_gradio_call
from modules.shared import opts
from modules.shared import opts
from modules.ui_components import FormRow
from modules.ui_components import FormRow
@@ -157,6 +157,17 @@ class UiSettings:
                with gr.TabItem("Defaults", id="defaults", elem_id="settings_tab_defaults"):
                with gr.TabItem("Defaults", id="defaults", elem_id="settings_tab_defaults"):
                    loadsave.create_ui()
                    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"):
                with gr.TabItem("Actions", id="actions", elem_id="settings_tab_actions"):
                    request_notifications = gr.Button(value='Request browser notifications', elem_id="request_notifications")
                    request_notifications = gr.Button(value='Request browser notifications', elem_id="request_notifications")
                    download_localization = gr.Button(value='Download localization template', elem_id="download_localization")
                    download_localization = gr.Button(value='Download localization template', elem_id="download_localization")
@@ -215,6 +226,21 @@ class UiSettings:
                outputs=[],
                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
        self.interface = settings_interface


    def add_quicksettings(self):
    def add_quicksettings(self):
+13 −0
Original line number Original line Diff line number Diff line
@@ -450,6 +450,19 @@ table.popup-table .link{
    opacity: 0.75;
    opacity: 0.75;
}
}


#sysinfo_download a.sysinfo_big_link{
    font-size: 24pt;
}

#sysinfo_download a{
    text-decoration: underline;
}

#sysinfo_validity{
    font-size: 18pt;
}


/* live preview */
/* live preview */
.progressDiv{
.progressDiv{
    position: relative;
    position: relative;