Make --debuginfo more like [p]debuginfo (#5662)

This commit is contained in:
jack1142 2022-04-09 19:34:12 +02:00 committed by GitHub
parent bc9f34c04b
commit c9f1a45854
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 189 additions and 141 deletions

View File

@ -18,7 +18,7 @@ import sys
from argparse import Namespace from argparse import Namespace
from copy import deepcopy from copy import deepcopy
from pathlib import Path from pathlib import Path
from typing import NoReturn from typing import Any, Awaitable, Callable, NoReturn, Union
import discord import discord
import rich import rich
@ -29,6 +29,7 @@ from redbot.core.bot import Red, ExitCodes, _NoOwnerSet
from redbot.core.cli import interactive_config, confirm, parse_cli_flags from redbot.core.cli import interactive_config, confirm, parse_cli_flags
from redbot.setup import get_data_dir, get_name, save_config from redbot.setup import get_data_dir, get_name, save_config
from redbot.core import data_manager, drivers from redbot.core import data_manager, drivers
from redbot.core._debuginfo import DebugInfo
from redbot.core._sharedlibdeprecation import SharedLibImportWarner from redbot.core._sharedlibdeprecation import SharedLibImportWarner
@ -62,42 +63,9 @@ def list_instances():
sys.exit(0) sys.exit(0)
def debug_info(): async def debug_info(*args: Any) -> None:
"""Shows debug information useful for debugging.""" """Shows debug information useful for debugging."""
if sys.platform == "linux": print(await DebugInfo().get_text())
import distro # pylint: disable=import-error
IS_WINDOWS = os.name == "nt"
IS_MAC = sys.platform == "darwin"
IS_LINUX = sys.platform == "linux"
pyver = sys.version
pipver = pip.__version__
redver = __version__
dpy_version = discord.__version__
if IS_WINDOWS:
os_info = platform.uname()
osver = "{} {} (version {})".format(os_info.system, os_info.release, os_info.version)
elif IS_MAC:
os_info = platform.mac_ver()
osver = "Mac OSX {} {}".format(os_info[0], os_info[2])
else:
osver = f"{distro.name()} {distro.version()}".strip()
user_who_ran = getpass.getuser()
info = (
"Debug Info for Red\n\n"
+ "Red version: {}\n".format(redver)
+ "Python version: {}\n".format(pyver)
+ "Python executable: {}\n".format(sys.executable)
+ "Discord.py version: {}\n".format(dpy_version)
+ "Pip version: {}\n".format(pipver)
+ "OS version: {}\n".format(osver)
+ "System arch: {}\n".format(platform.machine())
+ "User: {}\n".format(user_who_ran)
+ "Metadata file: {}\n".format(data_manager.config_file)
)
print(info)
sys.exit(0)
async def edit_instance(red, cli_flags): async def edit_instance(red, cli_flags):
@ -291,18 +259,25 @@ def _copy_data(data):
return True return True
def handle_edit(cli_flags: Namespace): def early_exit_runner(
cli_flags: Namespace,
func: Union[Callable[[], Awaitable[Any]], Callable[[Red, Namespace], Awaitable[Any]]],
) -> None:
""" """
This one exists to not log all the things like it's a full run of the bot. This one exists to not log all the things like it's a full run of the bot.
""" """
loop = asyncio.new_event_loop() loop = asyncio.new_event_loop()
asyncio.set_event_loop(loop) asyncio.set_event_loop(loop)
data_manager.load_basic_configuration(cli_flags.instance_name)
red = Red(cli_flags=cli_flags, description="Red V3", dm_help=None)
try: try:
if not cli_flags.instance_name:
loop.run_until_complete(func())
return
data_manager.load_basic_configuration(cli_flags.instance_name)
red = Red(cli_flags=cli_flags, description="Red V3", dm_help=None)
driver_cls = drivers.get_driver_class() driver_cls = drivers.get_driver_class()
loop.run_until_complete(driver_cls.initialize(**data_manager.storage_details())) loop.run_until_complete(driver_cls.initialize(**data_manager.storage_details()))
loop.run_until_complete(edit_instance(red, cli_flags)) loop.run_until_complete(func(red, cli_flags))
loop.run_until_complete(driver_cls.teardown()) loop.run_until_complete(driver_cls.teardown())
except (KeyboardInterrupt, EOFError): except (KeyboardInterrupt, EOFError):
print("Aborted!") print("Aborted!")
@ -430,7 +405,7 @@ def handle_early_exit_flags(cli_flags: Namespace):
print("Current Version: {}".format(__version__)) print("Current Version: {}".format(__version__))
sys.exit(0) sys.exit(0)
elif cli_flags.debuginfo: elif cli_flags.debuginfo:
debug_info() early_exit_runner(cli_flags, debug_info)
elif not cli_flags.instance_name and (not cli_flags.no_instance or cli_flags.edit): elif not cli_flags.instance_name and (not cli_flags.no_instance or cli_flags.edit):
print("Error: No instance name was provided!") print("Error: No instance name was provided!")
sys.exit(1) sys.exit(1)
@ -502,7 +477,7 @@ def main():
cli_flags = parse_cli_flags(sys.argv[1:]) cli_flags = parse_cli_flags(sys.argv[1:])
handle_early_exit_flags(cli_flags) handle_early_exit_flags(cli_flags)
if cli_flags.edit: if cli_flags.edit:
handle_edit(cli_flags) early_exit_runner(cli_flags, edit_instance)
return return
try: try:
loop = asyncio.new_event_loop() loop = asyncio.new_event_loop()

170
redbot/core/_debuginfo.py Normal file
View File

@ -0,0 +1,170 @@
from __future__ import annotations
import getpass
import os
import platform
import sys
from typing import Optional
import discord
import pip
import psutil
from redbot import __version__
from redbot.core import data_manager
from redbot.core.bot import Red
from redbot.core.utils.chat_formatting import box
def noop_box(text: str, **kwargs) -> str:
return text
def _datasize(num: int):
for unit in ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB"]:
if abs(num) < 1024.0:
return "{0:.1f}{1}".format(num, unit)
num /= 1024.0
return "{0:.1f}{1}".format(num, "YB")
class DebugInfoSection:
def __init__(self, section_name: str, *section_parts: str) -> None:
self.section_name = section_name
self.section_parts = section_parts
def get_command_text(self) -> str:
parts = [box(f"## {self.section_name}:", lang="md")]
for part in self.section_parts:
parts.append(box(part))
return "".join(parts)
def get_cli_text(self) -> str:
parts = [f"\x1b[32m## {self.section_name}:\x1b[0m"]
for part in self.section_parts:
parts.append(part)
return "\n".join(parts)
class DebugInfo:
def __init__(self, bot: Optional[Red] = None) -> None:
self.bot = bot
async def get_text(self) -> str:
if self.bot is None:
return await self.get_cli_text()
else:
return await self.get_command_text()
async def get_cli_text(self) -> str:
parts = ["\x1b[31m# Debug Info for Red:\x1b[0m"]
for section in (
self._get_system_metadata_section(),
self._get_os_variables_section(),
await self._get_red_vars_section(),
):
parts.append("")
parts.append(section.get_cli_text())
return "\n".join(parts)
async def get_command_text(self) -> str:
parts = [box("# Debug Info for Red:", lang="md")]
for section in (
self._get_system_metadata_section(),
self._get_os_variables_section(),
await self._get_red_vars_section(),
):
parts.append("\n")
parts.append(section.get_command_text())
return "".join(parts)
def _get_system_metadata_section(self) -> DebugInfoSection:
memory_ram = psutil.virtual_memory()
ram_string = "{used}/{total} ({percent}%)".format(
used=_datasize(memory_ram.used),
total=_datasize(memory_ram.total),
percent=memory_ram.percent,
)
return DebugInfoSection(
"System Metadata",
f"CPU Cores: {psutil.cpu_count()} ({platform.machine()})\nRAM: {ram_string}",
)
def _get_os_variables_section(self) -> DebugInfoSection:
IS_WINDOWS = os.name == "nt"
IS_MAC = sys.platform == "darwin"
IS_LINUX = sys.platform == "linux"
python_version = ".".join(map(str, sys.version_info[:3]))
pyver = f"{python_version} ({platform.architecture()[0]})"
pipver = pip.__version__
redver = __version__
dpy_version = discord.__version__
if IS_WINDOWS:
os_info = platform.uname()
osver = f"{os_info.system} {os_info.release} (version {os_info.version})"
elif IS_MAC:
os_info = platform.mac_ver()
osver = f"Mac OSX {os_info[0]} {os_info[2]}"
elif IS_LINUX:
import distro
osver = f"{distro.name()} {distro.version()}".strip()
else:
osver = "Could not parse OS, report this on Github."
user_who_ran = getpass.getuser()
resp_os = f"OS version: {osver}\nUser: {user_who_ran}\n" # Ran where off to?!
resp_py_metadata = (
f"Python executable: {sys.executable}\n"
f"Python version: {pyver}\n"
f"Pip version: {pipver}\n"
)
resp_red_metadata = f"Red version: {redver}\nDiscord.py version: {dpy_version}"
return DebugInfoSection(
"OS variables",
resp_os,
resp_py_metadata,
resp_red_metadata,
)
async def _get_red_vars_section(self) -> DebugInfoSection:
if data_manager.instance_name is None:
return DebugInfoSection(
"Red variables",
f"Metadata file: {data_manager.config_file}",
)
parts = [f"Instance name: {data_manager.instance_name}"]
if self.bot is not None:
owners = []
for uid in self.bot.owner_ids:
try:
u = await self.bot.get_or_fetch_user(uid)
owners.append(f"{u.id} ({u})")
except discord.HTTPException:
owners.append(f"{uid} (Unresolvable)")
owners_string = ", ".join(owners) or "None"
parts.append(f"Owner(s): {', '.join(owners) or 'None'}")
if self.bot is not None:
disabled_intents = (
", ".join(
intent_name.replace("_", " ").title()
for intent_name, enabled in self.bot.intents
if not enabled
)
or "None"
)
parts.append(f"Disabled intents: {disabled_intents}")
parts.append(f"Storage type: {data_manager.storage_type()}")
parts.append(f"Data path: {data_manager.basic_config['DATA_PATH']}")
parts.append(f"Metadata file: {data_manager.config_file}")
return DebugInfoSection(
"Red variables",
"\n".join(parts),
)

View File

@ -4091,106 +4091,9 @@ class Core(commands.commands._RuleDropper, commands.Cog, CoreLogic):
@checks.is_owner() @checks.is_owner()
async def debuginfo(self, ctx: commands.Context): async def debuginfo(self, ctx: commands.Context):
"""Shows debug information useful for debugging.""" """Shows debug information useful for debugging."""
from redbot.core._debuginfo import DebugInfo
if sys.platform == "linux": await ctx.send(await DebugInfo(self.bot).get_text())
import distro # pylint: disable=import-error
IS_WINDOWS = os.name == "nt"
IS_MAC = sys.platform == "darwin"
IS_LINUX = sys.platform == "linux"
python_version = ".".join(map(str, sys.version_info[:3]))
pyver = f"{python_version} ({platform.architecture()[0]})"
pipver = pip.__version__
redver = red_version_info
dpy_version = discord.__version__
if IS_WINDOWS:
os_info = platform.uname()
osver = f"{os_info.system} {os_info.release} (version {os_info.version})"
elif IS_MAC:
os_info = platform.mac_ver()
osver = f"Mac OSX {os_info[0]} {os_info[2]}"
elif IS_LINUX:
osver = f"{distro.name()} {distro.version()}".strip()
else:
osver = "Could not parse OS, report this on Github."
user_who_ran = getpass.getuser()
driver = storage_type()
from redbot.core.data_manager import basic_config, config_file
data_path = Path(basic_config["DATA_PATH"])
disabled_intents = (
", ".join(
intent_name.replace("_", " ").title()
for intent_name, enabled in self.bot.intents
if not enabled
)
or "None"
)
def _datasize(num: int):
for unit in ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB"]:
if abs(num) < 1024.0:
return "{0:.1f}{1}".format(num, unit)
num /= 1024.0
return "{0:.1f}{1}".format(num, "YB")
memory_ram = psutil.virtual_memory()
ram_string = "{used}/{total} ({percent}%)".format(
used=_datasize(memory_ram.used),
total=_datasize(memory_ram.total),
percent=memory_ram.percent,
)
owners = []
for uid in self.bot.owner_ids:
try:
u = await self.bot.get_or_fetch_user(uid)
owners.append(f"{u.id} ({u})")
except discord.HTTPException:
owners.append(f"{uid} (Unresolvable)")
owners_string = ", ".join(owners) or "None"
resp_intro = "# Debug Info for Red:"
resp_system_intro = "## System Metadata:"
resp_system = (
f"CPU Cores: {psutil.cpu_count()} ({platform.machine()})\nRAM: {ram_string}\n"
)
resp_os_intro = "## OS Variables:"
resp_os = f"OS version: {osver}\nUser: {user_who_ran}\n" # Ran where off to?!
resp_py_metadata = (
f"Python executable: {sys.executable}\n"
f"Python version: {pyver}\n"
f"Pip version: {pipver}\n"
)
resp_red_metadata = f"Red version: {redver}\nDiscord.py version: {dpy_version}\n"
resp_red_vars_intro = "## Red variables:"
resp_red_vars = (
f"Instance name: {data_manager.instance_name}\n"
f"Owner(s): {owners_string}\n"
f"Storage type: {driver}\n"
f"Disabled intents: {disabled_intents}\n"
f"Data path: {data_path}\n"
f"Metadata file: {config_file}"
)
response = (
box(resp_intro, lang="md"),
"\n",
box(resp_system_intro, lang="md"),
box(resp_system),
"\n",
box(resp_os_intro, lang="md"),
box(resp_os),
box(resp_py_metadata),
box(resp_red_metadata),
"\n",
box(resp_red_vars_intro, lang="md"),
box(resp_red_vars),
)
await ctx.send("".join(response))
# You may ask why this command is owner-only, # You may ask why this command is owner-only,
# cause after all it could be quite useful to guild owners! # cause after all it could be quite useful to guild owners!