Refactor: make "sys_info" an internal module, rename for clarity

- Move sys_info into an internal module to avoid having so many
  top level modules for Blender's internal functionality.
- Rename system_info sub-modules that pre-fill URL's for clarity.
- Move top-level exception handling into the operator.
- Report an error if an unexpected exception occurs.
- Use `Exception` instead of `BaseException` as there is no reason to
  catch the additional exceptions.
- Remove use of sys_info from the command line example,
  replace with in-lined system info.
This commit is contained in:
Campbell Barton
2024-09-06 11:11:26 +10:00
parent 73ac0275c6
commit 97179b10fb
9 changed files with 364 additions and 281 deletions

View File

@@ -5,25 +5,74 @@ Registering commands makes it possible to conveniently expose command line
functionality via commands passed to (``-c`` / ``--command``). functionality via commands passed to (``-c`` / ``--command``).
""" """
import sys
import os import os
import bpy import bpy
def sysinfo_print():
"""
Report basic system information.
"""
import pprint
import platform
import textwrap
width = 80
indent = 2
print("Blender {:s}".format(bpy.app.version_string))
print("Running on: {:s}-{:s}".format(platform.platform(), platform.machine()))
print("Processors: {!r}".format(os.cpu_count()))
print()
# Dump `bpy.app`.
for attr in dir(bpy.app):
if attr.startswith("_"):
continue
# Overly verbose.
if attr in {"handlers", "build_cflags", "build_cxxflags"}:
continue
value = getattr(bpy.app, attr)
if attr.startswith("build_"):
pass
elif isinstance(value, tuple):
pass
else:
# Otherwise ignore.
continue
if isinstance(value, bytes):
value = value.decode("utf-8", errors="ignore")
if isinstance(value, str):
pass
elif isinstance(value, tuple) and hasattr(value, "__dir__"):
value = {
attr_sub: value_sub
for attr_sub in dir(value)
# Exclude built-ins.
if not attr_sub.startswith(("_", "n_"))
# Exclude methods.
if not callable(value_sub := getattr(value, attr_sub))
}
value = pprint.pformat(value, indent=0, width=width)
else:
value = pprint.pformat(value, indent=0, width=width)
print("{:s}:\n{:s}\n".format(attr, textwrap.indent(value, " " * indent)))
def sysinfo_command(argv): def sysinfo_command(argv):
import tempfile import tempfile
import sys_info
if argv and argv[0] == "--help": if argv and argv[0] == "--help":
print("Print system information & exit!") print("Print system information & exit!")
return 0 return 0
with tempfile.TemporaryDirectory() as tempdir: sysinfo_print()
filepath = os.path.join(tempdir, "system_info.txt")
sys_info.write_sysinfo(filepath)
with open(filepath, "r", encoding="utf-8") as fh:
sys.stdout.write(fh.read())
return 0 return 0

View File

@@ -4,7 +4,7 @@
BASE_DIR=$(dirname "$0") BASE_DIR=$(dirname "$0")
PYTHON_BIN="$BASE_DIR/@BLENDER_VERSION@/python/bin/@PYTHON_EXECUTABLE_NAME_ONLY@" PYTHON_BIN="$BASE_DIR/@BLENDER_VERSION@/python/bin/@PYTHON_EXECUTABLE_NAME_ONLY@"
SYSTEM_INFO_STARTUP_PY="$BASE_DIR/@BLENDER_VERSION@/scripts/modules/_bpy_internal/system_info/startup.py" SYSTEM_INFO_STARTUP_PY="$BASE_DIR/@BLENDER_VERSION@/scripts/modules/_bpy_internal/system_info/url_prefill_startup.py"
if test -f "$PYTHON_BIN"; then if test -f "$PYTHON_BIN"; then
exec "$PYTHON_BIN" -I "$SYSTEM_INFO_STARTUP_PY" exec "$PYTHON_BIN" -I "$SYSTEM_INFO_STARTUP_PY"
fi fi

View File

@@ -4,7 +4,7 @@
BASE_DIR=$(dirname "$0") BASE_DIR=$(dirname "$0")
PYTHON_BIN="$BASE_DIR/@BLENDER_VERSION@/python/bin/@PYTHON_EXECUTABLE_NAME_ONLY@" PYTHON_BIN="$BASE_DIR/@BLENDER_VERSION@/python/bin/@PYTHON_EXECUTABLE_NAME_ONLY@"
SYSTEM_INFO_STARTUP_PY="$BASE_DIR/@BLENDER_VERSION@/scripts/modules/_bpy_internal/system_info/startup.py" SYSTEM_INFO_STARTUP_PY="$BASE_DIR/@BLENDER_VERSION@/scripts/modules/_bpy_internal/system_info/url_prefill_startup.py"
if test -f "$PYTHON_BIN"; then if test -f "$PYTHON_BIN"; then
exec "$PYTHON_BIN" -I "$SYSTEM_INFO_STARTUP_PY" exec "$PYTHON_BIN" -I "$SYSTEM_INFO_STARTUP_PY"
fi fi

View File

@@ -5,7 +5,7 @@ set BLENDER_VERSION_FOLDER=%BLENDER_INSTALL_DIRECTORY%@BLENDER_VERSION@
set PYTHON_BIN=%BLENDER_VERSION_FOLDER%\python\bin\@PYTHON_EXECUTABLE_NAME_ONLY@ set PYTHON_BIN=%BLENDER_VERSION_FOLDER%\python\bin\@PYTHON_EXECUTABLE_NAME_ONLY@
if exist "%PYTHON_BIN%" ( if exist "%PYTHON_BIN%" (
"%PYTHON_BIN%" -I "%BLENDER_VERSION_FOLDER%\scripts\modules\_bpy_internal\system_info\startup.py" "%PYTHON_BIN%" -I "%BLENDER_VERSION_FOLDER%\scripts\modules\_bpy_internal\system_info\url_prefill_startup.py"
exit /b exit /b
) )

View File

@@ -0,0 +1,263 @@
# SPDX-FileCopyrightText: 2010-2023 Blender Authors
#
# SPDX-License-Identifier: GPL-2.0-or-later
# Function for extracting info from Blenders system information
# (sometimes useful to include in bug reports).
# Called by the operator `WM_OT_sysinfo`.
__all__ = (
"write",
)
def write(output):
# Writes into `output`, a file-like object.
import sys
import platform
import subprocess
import bpy
import gpu
# pretty repr
def prepr(v):
r = repr(v)
vt = type(v)
if vt is bytes:
r = r[2:-1]
elif vt is list or vt is tuple:
r = r[1:-1]
return r
header = "= Blender {:s} System Information =\n".format(bpy.app.version_string)
lilies = "{:s}\n\n".format((len(header) - 1) * "=")
output.write(lilies[:-1])
output.write(header)
output.write(lilies)
def title(text):
return "\n{:s}:\n{:s}".format(text, lilies)
# build info
output.write(title("Blender"))
output.write(
"version: {:s}, branch: {:s}, commit date: {:s} {:s}, hash: {:s}, type: {:s}\n".format(
bpy.app.version_string,
prepr(bpy.app.build_branch),
prepr(bpy.app.build_commit_date),
prepr(bpy.app.build_commit_time),
prepr(bpy.app.build_hash),
prepr(bpy.app.build_type),
)
)
output.write("build date: {:s}, {:s}\n".format(prepr(bpy.app.build_date), prepr(bpy.app.build_time)))
output.write("platform: {:s}\n".format(prepr(platform.platform())))
output.write("binary path: {:s}\n".format(prepr(bpy.app.binary_path)))
output.write("build cflags: {:s}\n".format(prepr(bpy.app.build_cflags)))
output.write("build cxxflags: {:s}\n".format(prepr(bpy.app.build_cxxflags)))
output.write("build linkflags: {:s}\n".format(prepr(bpy.app.build_linkflags)))
output.write("build system: {:s}\n".format(prepr(bpy.app.build_system)))
# Windowing Environment (include when dynamically selectable).
from _bpy import _ghost_backend
ghost_backend = _ghost_backend()
if ghost_backend not in {'NONE', 'DEFAULT'}:
output.write("windowing environment: {:s}\n".format(prepr(ghost_backend)))
del _ghost_backend, ghost_backend
# Python info.
output.write(title("Python"))
output.write("version: {:s}\n".format(sys.version.replace("\n", " ")))
output.write("file system encoding: {:s}:{:s}\n".format(
sys.getfilesystemencoding(),
sys.getfilesystemencodeerrors(),
))
output.write("paths:\n")
for p in sys.path:
output.write("\t{!r}\n".format(p))
output.write(title("Python (External Binary)"))
output.write("binary path: {:s}\n".format(prepr(sys.executable)))
try:
py_ver = prepr(subprocess.check_output([
sys.executable,
"--version",
]).strip())
except Exception as ex:
py_ver = str(ex)
output.write("version: {:s}\n".format(py_ver))
del py_ver
output.write(title("Directories"))
output.write("scripts:\n")
for p in bpy.utils.script_paths():
output.write("\t{!r}\n".format(p))
output.write("user scripts: {!r}\n".format(bpy.utils.script_path_user()))
output.write("pref scripts:\n")
for p in bpy.utils.script_paths_pref():
output.write("\t{!r}\n".format(p))
output.write("datafiles: {!r}\n".format(bpy.utils.user_resource('DATAFILES')))
output.write("config: {!r}\n".format(bpy.utils.user_resource('CONFIG')))
output.write("scripts: {!r}\n".format(bpy.utils.user_resource('SCRIPTS')))
output.write("extensions: {!r}\n".format(bpy.utils.user_resource('EXTENSIONS')))
output.write("tempdir: {!r}\n".format(bpy.app.tempdir))
output.write(title("FFmpeg"))
ffmpeg = bpy.app.ffmpeg
if ffmpeg.supported:
for lib in ("avcodec", "avdevice", "avformat", "avutil", "swscale"):
output.write(
"{:s}:{:s}{!r}\n".format(
lib,
" " * (10 - len(lib)),
getattr(ffmpeg, lib + "_version_string"),
)
)
else:
output.write("Blender was built without FFmpeg support\n")
if bpy.app.build_options.sdl:
output.write(title("SDL"))
output.write("Version: {:s}\n".format(bpy.app.sdl.version_string))
output.write("Loading method: ")
if bpy.app.build_options.sdl_dynload:
output.write("dynamically loaded by Blender (WITH_SDL_DYNLOAD=ON)\n")
else:
output.write("linked (WITH_SDL_DYNLOAD=OFF)\n")
if not bpy.app.sdl.available:
output.write("WARNING: Blender could not load SDL library\n")
output.write(title("Other Libraries"))
ocio = bpy.app.ocio
output.write("OpenColorIO: ")
if ocio.supported:
if ocio.version_string == "fallback":
output.write("Blender was built with OpenColorIO, "
"but it currently uses fallback color management.\n")
else:
output.write("{:s}\n".format(ocio.version_string))
else:
output.write("Blender was built without OpenColorIO support\n")
oiio = bpy.app.oiio
output.write("OpenImageIO: ")
if ocio.supported:
output.write("{:s}\n".format(oiio.version_string))
else:
output.write("Blender was built without OpenImageIO support\n")
output.write("OpenShadingLanguage: ")
if bpy.app.build_options.cycles:
if bpy.app.build_options.cycles_osl:
from _cycles import osl_version_string
output.write("{:s}\n".format(osl_version_string))
else:
output.write("Blender was built without OpenShadingLanguage support in Cycles\n")
else:
output.write("Blender was built without Cycles support\n")
opensubdiv = bpy.app.opensubdiv
output.write("OpenSubdiv: ")
if opensubdiv.supported:
output.write("{:s}\n".format(opensubdiv.version_string))
else:
output.write("Blender was built without OpenSubdiv support\n")
openvdb = bpy.app.openvdb
output.write("OpenVDB: ")
if openvdb.supported:
output.write("{:s}\n".format(openvdb.version_string))
else:
output.write("Blender was built without OpenVDB support\n")
alembic = bpy.app.alembic
output.write("Alembic: ")
if alembic.supported:
output.write("{:s}\n".format(alembic.version_string))
else:
output.write("Blender was built without Alembic support\n")
usd = bpy.app.usd
output.write("USD: ")
if usd.supported:
output.write("{:s}\n".format(usd.version_string))
else:
output.write("Blender was built without USD support\n")
if not bpy.app.build_options.sdl:
output.write("SDL: Blender was built without SDL support\n")
if bpy.app.background:
output.write("\nGPU: missing, background mode\n")
else:
output.write(title("GPU"))
output.write("renderer:\t{!r}\n".format(gpu.platform.renderer_get()))
output.write("vendor:\t\t{!r}\n".format(gpu.platform.vendor_get()))
output.write("version:\t{!r}\n".format(gpu.platform.version_get()))
output.write("device type:\t{!r}\n".format(gpu.platform.device_type_get()))
output.write("backend type:\t{!r}\n".format(gpu.platform.backend_type_get()))
output.write("extensions:\n")
glext = sorted(gpu.capabilities.extensions_get())
for l in glext:
output.write("\t{:s}\n".format(l))
output.write(title("Implementation Dependent GPU Limits"))
output.write("Maximum Batch Vertices:\t{:d}\n".format(
gpu.capabilities.max_batch_vertices_get(),
))
output.write("Maximum Batch Indices:\t{:d}\n".format(
gpu.capabilities.max_batch_indices_get(),
))
output.write("\nGLSL:\n")
output.write("Maximum Varying Floats:\t{:d}\n".format(
gpu.capabilities.max_varying_floats_get(),
))
output.write("Maximum Vertex Attributes:\t{:d}\n".format(
gpu.capabilities.max_vertex_attribs_get(),
))
output.write("Maximum Vertex Uniform Components:\t{:d}\n".format(
gpu.capabilities.max_uniforms_vert_get(),
))
output.write("Maximum Fragment Uniform Components:\t{:d}\n".format(
gpu.capabilities.max_uniforms_frag_get(),
))
output.write("Maximum Vertex Image Units:\t{:d}\n".format(
gpu.capabilities.max_textures_vert_get(),
))
output.write("Maximum Fragment Image Units:\t{:d}\n".format(
gpu.capabilities.max_textures_frag_get(),
))
output.write("Maximum Pipeline Image Units:\t{:d}\n".format(
gpu.capabilities.max_textures_get(),
))
output.write("Maximum Image Units:\t{:d}\n".format(
gpu.capabilities.max_images_get(),
))
if bpy.app.build_options.cycles:
import cycles
output.write(title("Cycles"))
output.write(cycles.engine.system_info())
import addon_utils
addon_utils.modules()
output.write(title("Enabled add-ons"))
for addon in bpy.context.preferences.addons.keys():
addon_mod = addon_utils.addons_fake_modules.get(addon, None)
if addon_mod is None:
output.write("{:s} (MISSING)\n".format(addon))
else:
output.write(
"{:s} (version: {:s}, path: {!r})\n".format(
addon,
str(addon_mod.bl_info.get("version", "UNKNOWN")),
addon_mod.__file__,
)
)

View File

@@ -4,7 +4,12 @@
# Keep the information collected in this script synchronized with `startup.py`. # Keep the information collected in this script synchronized with `startup.py`.
def url_prefill_from_blender(*, addon_info=None): __all__ = (
"url_from_blender",
)
def url_from_blender(*, addon_info=None):
import bpy import bpy
import gpu import gpu
import struct import struct

View File

@@ -5,7 +5,15 @@
# Keep the information collected in this script synchronized with `runtime.py`. # Keep the information collected in this script synchronized with `runtime.py`.
def prefill_bug_report_info() -> int: # NOTE: this can run as a standalone script, called directly from Python
# (even though it's located inside a package).
__all__ = (
"url_from_blender",
)
def url_from_blender() -> str:
from typing import ( from typing import (
Dict, Dict,
Optional, Optional,
@@ -15,7 +23,7 @@ def prefill_bug_report_info() -> int:
import struct import struct
import platform import platform
import subprocess import subprocess
import webbrowser import sys
import urllib.parse import urllib.parse
from pathlib import Path from pathlib import Path
@@ -54,7 +62,7 @@ def prefill_bug_report_info() -> int:
) )
except Exception as ex: except Exception as ex:
sys.stderr.write("{:s}\n".format(str(ex))) sys.stderr.write("{:s}\n".format(str(ex)))
return 1 return ""
text = blender_output.stdout text = blender_output.stdout
@@ -78,7 +86,7 @@ def prefill_bug_report_info() -> int:
# No valid Blender info could be found. # No valid Blender info could be found.
print("Blender did not provide any build information. Blender may be corrupt or blocked from running.") print("Blender did not provide any build information. Blender may be corrupt or blocked from running.")
print("Please try reinstalling Blender and double check your anti-virus isn't blocking it from running.") print("Please try reinstalling Blender and double check your anti-virus isn't blocking it from running.")
return 1 return ""
query_params["broken_version"] = ( query_params["broken_version"] = (
"{version:s}, branch: {branch:s}, commit date: {commit_date:s} {commit_time:s}, hash `{build_hash:s}`".format( "{version:s}, branch: {branch:s}, commit date: {commit_date:s} {commit_time:s}, hash `{build_hash:s}`".format(
@@ -86,12 +94,19 @@ def prefill_bug_report_info() -> int:
) )
) )
query_str = urllib.parse.urlencode(query_params) return "https://redirect.blender.org/?{:s}".format(urllib.parse.urlencode(query_params))
webbrowser.open("https://redirect.blender.org/?{:s}".format(query_str))
def main() -> int:
import webbrowser
if not (url := url_from_blender()):
return 1
webbrowser.open(url)
return 0 return 0
if __name__ == "__main__": if __name__ == "__main__":
import sys import sys
sys.exit(prefill_bug_report_info()) sys.exit(main())

View File

@@ -1,259 +0,0 @@
# SPDX-FileCopyrightText: 2010-2023 Blender Authors
#
# SPDX-License-Identifier: GPL-2.0-or-later
# classes for extracting info from blenders internal classes
def write_sysinfo(filepath):
import sys
import platform
import subprocess
import bpy
import gpu
# pretty repr
def prepr(v):
r = repr(v)
vt = type(v)
if vt is bytes:
r = r[2:-1]
elif vt is list or vt is tuple:
r = r[1:-1]
return r
with open(filepath, 'w', encoding="utf-8") as output:
try:
header = "= Blender {:s} System Information =\n".format(bpy.app.version_string)
lilies = "{:s}\n\n".format((len(header) - 1) * "=")
output.write(lilies[:-1])
output.write(header)
output.write(lilies)
def title(text):
return "\n{:s}:\n{:s}".format(text, lilies)
# build info
output.write(title("Blender"))
output.write(
"version: {:s}, branch: {:s}, commit date: {:s} {:s}, hash: {:s}, type: {:s}\n".format(
bpy.app.version_string,
prepr(bpy.app.build_branch),
prepr(bpy.app.build_commit_date),
prepr(bpy.app.build_commit_time),
prepr(bpy.app.build_hash),
prepr(bpy.app.build_type),
)
)
output.write("build date: {:s}, {:s}\n".format(prepr(bpy.app.build_date), prepr(bpy.app.build_time)))
output.write("platform: {:s}\n".format(prepr(platform.platform())))
output.write("binary path: {:s}\n".format(prepr(bpy.app.binary_path)))
output.write("build cflags: {:s}\n".format(prepr(bpy.app.build_cflags)))
output.write("build cxxflags: {:s}\n".format(prepr(bpy.app.build_cxxflags)))
output.write("build linkflags: {:s}\n".format(prepr(bpy.app.build_linkflags)))
output.write("build system: {:s}\n".format(prepr(bpy.app.build_system)))
# Windowing Environment (include when dynamically selectable).
from _bpy import _ghost_backend
ghost_backend = _ghost_backend()
if ghost_backend not in {'NONE', 'DEFAULT'}:
output.write("windowing environment: {:s}\n".format(prepr(ghost_backend)))
del _ghost_backend, ghost_backend
# Python info.
output.write(title("Python"))
output.write("version: {:s}\n".format(sys.version.replace("\n", " ")))
output.write("file system encoding: {:s}:{:s}\n".format(
sys.getfilesystemencoding(),
sys.getfilesystemencodeerrors(),
))
output.write("paths:\n")
for p in sys.path:
output.write("\t{!r}\n".format(p))
output.write(title("Python (External Binary)"))
output.write("binary path: {:s}\n".format(prepr(sys.executable)))
try:
py_ver = prepr(subprocess.check_output([
sys.executable,
"--version",
]).strip())
except BaseException as ex:
py_ver = str(ex)
output.write("version: {:s}\n".format(py_ver))
del py_ver
output.write(title("Directories"))
output.write("scripts:\n")
for p in bpy.utils.script_paths():
output.write("\t{!r}\n".format(p))
output.write("user scripts: {!r}\n".format(bpy.utils.script_path_user()))
output.write("pref scripts:\n")
for p in bpy.utils.script_paths_pref():
output.write("\t{!r}\n".format(p))
output.write("datafiles: {!r}\n".format(bpy.utils.user_resource('DATAFILES')))
output.write("config: {!r}\n".format(bpy.utils.user_resource('CONFIG')))
output.write("scripts: {!r}\n".format(bpy.utils.user_resource('SCRIPTS')))
output.write("extensions: {!r}\n".format(bpy.utils.user_resource('EXTENSIONS')))
output.write("tempdir: {!r}\n".format(bpy.app.tempdir))
output.write(title("FFmpeg"))
ffmpeg = bpy.app.ffmpeg
if ffmpeg.supported:
for lib in ("avcodec", "avdevice", "avformat", "avutil", "swscale"):
output.write(
"{:s}:{:s}{!r}\n".format(
lib,
" " * (10 - len(lib)),
getattr(ffmpeg, lib + "_version_string"),
)
)
else:
output.write("Blender was built without FFmpeg support\n")
if bpy.app.build_options.sdl:
output.write(title("SDL"))
output.write("Version: {:s}\n".format(bpy.app.sdl.version_string))
output.write("Loading method: ")
if bpy.app.build_options.sdl_dynload:
output.write("dynamically loaded by Blender (WITH_SDL_DYNLOAD=ON)\n")
else:
output.write("linked (WITH_SDL_DYNLOAD=OFF)\n")
if not bpy.app.sdl.available:
output.write("WARNING: Blender could not load SDL library\n")
output.write(title("Other Libraries"))
ocio = bpy.app.ocio
output.write("OpenColorIO: ")
if ocio.supported:
if ocio.version_string == "fallback":
output.write("Blender was built with OpenColorIO, "
"but it currently uses fallback color management.\n")
else:
output.write("{:s}\n".format(ocio.version_string))
else:
output.write("Blender was built without OpenColorIO support\n")
oiio = bpy.app.oiio
output.write("OpenImageIO: ")
if ocio.supported:
output.write("{:s}\n".format(oiio.version_string))
else:
output.write("Blender was built without OpenImageIO support\n")
output.write("OpenShadingLanguage: ")
if bpy.app.build_options.cycles:
if bpy.app.build_options.cycles_osl:
from _cycles import osl_version_string
output.write("{:s}\n".format(osl_version_string))
else:
output.write("Blender was built without OpenShadingLanguage support in Cycles\n")
else:
output.write("Blender was built without Cycles support\n")
opensubdiv = bpy.app.opensubdiv
output.write("OpenSubdiv: ")
if opensubdiv.supported:
output.write("{:s}\n".format(opensubdiv.version_string))
else:
output.write("Blender was built without OpenSubdiv support\n")
openvdb = bpy.app.openvdb
output.write("OpenVDB: ")
if openvdb.supported:
output.write("{:s}\n".format(openvdb.version_string))
else:
output.write("Blender was built without OpenVDB support\n")
alembic = bpy.app.alembic
output.write("Alembic: ")
if alembic.supported:
output.write("{:s}\n".format(alembic.version_string))
else:
output.write("Blender was built without Alembic support\n")
usd = bpy.app.usd
output.write("USD: ")
if usd.supported:
output.write("{:s}\n".format(usd.version_string))
else:
output.write("Blender was built without USD support\n")
if not bpy.app.build_options.sdl:
output.write("SDL: Blender was built without SDL support\n")
if bpy.app.background:
output.write("\nGPU: missing, background mode\n")
else:
output.write(title("GPU"))
output.write("renderer:\t{!r}\n".format(gpu.platform.renderer_get()))
output.write("vendor:\t\t{!r}\n".format(gpu.platform.vendor_get()))
output.write("version:\t{!r}\n".format(gpu.platform.version_get()))
output.write("device type:\t{!r}\n".format(gpu.platform.device_type_get()))
output.write("backend type:\t{!r}\n".format(gpu.platform.backend_type_get()))
output.write("extensions:\n")
glext = sorted(gpu.capabilities.extensions_get())
for l in glext:
output.write("\t{:s}\n".format(l))
output.write(title("Implementation Dependent GPU Limits"))
output.write("Maximum Batch Vertices:\t{:d}\n".format(
gpu.capabilities.max_batch_vertices_get(),
))
output.write("Maximum Batch Indices:\t{:d}\n".format(
gpu.capabilities.max_batch_indices_get(),
))
output.write("\nGLSL:\n")
output.write("Maximum Varying Floats:\t{:d}\n".format(
gpu.capabilities.max_varying_floats_get(),
))
output.write("Maximum Vertex Attributes:\t{:d}\n".format(
gpu.capabilities.max_vertex_attribs_get(),
))
output.write("Maximum Vertex Uniform Components:\t{:d}\n".format(
gpu.capabilities.max_uniforms_vert_get(),
))
output.write("Maximum Fragment Uniform Components:\t{:d}\n".format(
gpu.capabilities.max_uniforms_frag_get(),
))
output.write("Maximum Vertex Image Units:\t{:d}\n".format(
gpu.capabilities.max_textures_vert_get(),
))
output.write("Maximum Fragment Image Units:\t{:d}\n".format(
gpu.capabilities.max_textures_frag_get(),
))
output.write("Maximum Pipeline Image Units:\t{:d}\n".format(
gpu.capabilities.max_textures_get(),
))
output.write("Maximum Image Units:\t{:d}\n".format(
gpu.capabilities.max_images_get(),
))
if bpy.app.build_options.cycles:
import cycles
output.write(title("Cycles"))
output.write(cycles.engine.system_info())
import addon_utils
addon_utils.modules()
output.write(title("Enabled add-ons"))
for addon in bpy.context.preferences.addons.keys():
addon_mod = addon_utils.addons_fake_modules.get(addon, None)
if addon_mod is None:
output.write("{:s} (MISSING)\n".format(addon))
else:
output.write(
"{:s} (version: {:s}, path: {!r})\n".format(
addon,
str(addon_mod.bl_info.get("version", "UNKNOWN")),
addon_mod.__file__,
)
)
except BaseException as ex:
output.write("ERROR: {:s}\n".format(str(ex)))

View File

@@ -1086,8 +1086,8 @@ class WM_OT_url_open_preset(Operator):
) )
def _url_from_bug(self, _context): def _url_from_bug(self, _context):
from _bpy_internal.system_info.runtime import url_prefill_from_blender from _bpy_internal.system_info.url_prefill_runtime import url_from_blender
return url_prefill_from_blender() return url_from_blender()
def _url_from_release_notes(self, _context): def _url_from_release_notes(self, _context):
return "https://www.blender.org/download/releases/{:d}-{:d}/".format(*bpy.app.version[:2]) return "https://www.blender.org/download/releases/{:d}-{:d}/".format(*bpy.app.version[:2])
@@ -2197,8 +2197,18 @@ class WM_OT_sysinfo(Operator):
) )
def execute(self, _context): def execute(self, _context):
import sys_info from _bpy_internal.system_info.text_generate_runtime import write
sys_info.write_sysinfo(self.filepath) with open(self.filepath, "w", encoding="utf-8") as output:
try:
write(output)
except Exception as ex:
# Not expected to occur, simply forward the exception.
self.report({'ERROR'}, str(ex))
# Also write into the file (to avoid confusion).
output.write("ERROR: {:s}\n".format(str(ex)))
return {'CANCELLED'}
return {'FINISHED'} return {'FINISHED'}
def invoke(self, context, _event): def invoke(self, context, _event):