diff --git a/doc/python_api/examples/bpy.utils.register_cli_command.py b/doc/python_api/examples/bpy.utils.register_cli_command.py index ca03d5e8223..c8b5012c156 100644 --- a/doc/python_api/examples/bpy.utils.register_cli_command.py +++ b/doc/python_api/examples/bpy.utils.register_cli_command.py @@ -5,25 +5,74 @@ Registering commands makes it possible to conveniently expose command line functionality via commands passed to (``-c`` / ``--command``). """ -import sys import os 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): import tempfile - import sys_info if argv and argv[0] == "--help": print("Print system information & exit!") return 0 - with tempfile.TemporaryDirectory() as tempdir: - 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()) + sysinfo_print() return 0 diff --git a/release/darwin/scripts/blender-system-info.sh.in b/release/darwin/scripts/blender-system-info.sh.in index e7c965142ac..f718210b28b 100755 --- a/release/darwin/scripts/blender-system-info.sh.in +++ b/release/darwin/scripts/blender-system-info.sh.in @@ -4,7 +4,7 @@ BASE_DIR=$(dirname "$0") 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 exec "$PYTHON_BIN" -I "$SYSTEM_INFO_STARTUP_PY" fi diff --git a/release/freedesktop/scripts/blender-system-info.sh.in b/release/freedesktop/scripts/blender-system-info.sh.in index feee4747b21..c8e3c74ad6a 100755 --- a/release/freedesktop/scripts/blender-system-info.sh.in +++ b/release/freedesktop/scripts/blender-system-info.sh.in @@ -4,7 +4,7 @@ BASE_DIR=$(dirname "$0") 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 exec "$PYTHON_BIN" -I "$SYSTEM_INFO_STARTUP_PY" fi diff --git a/release/windows/batch/blender_system_info.cmd.in b/release/windows/batch/blender_system_info.cmd.in index b250d41219e..164ce46b714 100644 --- a/release/windows/batch/blender_system_info.cmd.in +++ b/release/windows/batch/blender_system_info.cmd.in @@ -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@ 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 ) diff --git a/scripts/modules/_bpy_internal/system_info/text_generate_runtime.py b/scripts/modules/_bpy_internal/system_info/text_generate_runtime.py new file mode 100644 index 00000000000..ffda2af3db1 --- /dev/null +++ b/scripts/modules/_bpy_internal/system_info/text_generate_runtime.py @@ -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__, + ) + ) diff --git a/scripts/modules/_bpy_internal/system_info/runtime.py b/scripts/modules/_bpy_internal/system_info/url_prefill_runtime.py similarity index 95% rename from scripts/modules/_bpy_internal/system_info/runtime.py rename to scripts/modules/_bpy_internal/system_info/url_prefill_runtime.py index f7550de01fd..5dbcdc24d5f 100644 --- a/scripts/modules/_bpy_internal/system_info/runtime.py +++ b/scripts/modules/_bpy_internal/system_info/url_prefill_runtime.py @@ -4,7 +4,12 @@ # 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 gpu import struct diff --git a/scripts/modules/_bpy_internal/system_info/startup.py b/scripts/modules/_bpy_internal/system_info/url_prefill_startup.py similarity index 87% rename from scripts/modules/_bpy_internal/system_info/startup.py rename to scripts/modules/_bpy_internal/system_info/url_prefill_startup.py index c14bdf450a6..28a0311d65e 100755 --- a/scripts/modules/_bpy_internal/system_info/startup.py +++ b/scripts/modules/_bpy_internal/system_info/url_prefill_startup.py @@ -5,7 +5,15 @@ # 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 ( Dict, Optional, @@ -15,7 +23,7 @@ def prefill_bug_report_info() -> int: import struct import platform import subprocess - import webbrowser + import sys import urllib.parse from pathlib import Path @@ -54,7 +62,7 @@ def prefill_bug_report_info() -> int: ) except Exception as ex: sys.stderr.write("{:s}\n".format(str(ex))) - return 1 + return "" text = blender_output.stdout @@ -78,7 +86,7 @@ def prefill_bug_report_info() -> int: # No valid Blender info could be found. 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.") - return 1 + return "" query_params["broken_version"] = ( "{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) - webbrowser.open("https://redirect.blender.org/?{:s}".format(query_str)) + return "https://redirect.blender.org/?{:s}".format(urllib.parse.urlencode(query_params)) + +def main() -> int: + import webbrowser + + if not (url := url_from_blender()): + return 1 + + webbrowser.open(url) return 0 if __name__ == "__main__": import sys - sys.exit(prefill_bug_report_info()) + sys.exit(main()) diff --git a/scripts/modules/sys_info.py b/scripts/modules/sys_info.py deleted file mode 100644 index 0d5888d31e3..00000000000 --- a/scripts/modules/sys_info.py +++ /dev/null @@ -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))) diff --git a/scripts/startup/bl_operators/wm.py b/scripts/startup/bl_operators/wm.py index 068c9e364f2..2f0f1626909 100644 --- a/scripts/startup/bl_operators/wm.py +++ b/scripts/startup/bl_operators/wm.py @@ -1086,8 +1086,8 @@ class WM_OT_url_open_preset(Operator): ) def _url_from_bug(self, _context): - from _bpy_internal.system_info.runtime import url_prefill_from_blender - return url_prefill_from_blender() + from _bpy_internal.system_info.url_prefill_runtime import url_from_blender + return url_from_blender() def _url_from_release_notes(self, _context): 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): - import sys_info - sys_info.write_sysinfo(self.filepath) + from _bpy_internal.system_info.text_generate_runtime import write + 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'} def invoke(self, context, _event):