diff --git a/pyproject.toml b/pyproject.toml index ef14902732..8315a80b40 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -23,7 +23,7 @@ dependencies = [ "ansys-platform-instancemanagement~=1.0", "appdirs>=1.4.0", "grpcio>=1.30.0", # tested up to grpcio==1.35 - "importlib-metadata >=4.0", + "importlib-metadata>=4.0", "matplotlib>=3.0.0", # for colormaps for pyvista "numpy>=1.14.0", "pexpect>=4.8.0 ; platform_system=='Linux'", diff --git a/requirements/requirements_tests.txt b/requirements/requirements_tests.txt index 07533b146d..7b1e0c4a16 100644 --- a/requirements/requirements_tests.txt +++ b/requirements/requirements_tests.txt @@ -5,4 +5,5 @@ pandas==1.4.3 pytest==7.1.2 pytest-cov==3.0.0 pyvista==0.34.1 +pyansys-tools-report==0.2.2 vtk==9.0.3 diff --git a/src/ansys/mapdl/core/misc.py b/src/ansys/mapdl/core/misc.py index 11e279d09f..24d04b8bbd 100644 --- a/src/ansys/mapdl/core/misc.py +++ b/src/ansys/mapdl/core/misc.py @@ -20,12 +20,12 @@ from ansys.mapdl.core import _HAS_PYVISTA, LOG try: - import scooby + import ansys.tools.report as pyansys_report - _HAS_SCOOBY = True + _HAS_PYANSYS_REPORT = True except ModuleNotFoundError: # pragma: no cover - LOG.debug("The module 'scooby' is not installed.") - _HAS_SCOOBY = False + LOG.debug("The package 'pyansys-tools-report' is not installed.") + _HAS_PYANSYS_REPORT = False # path of this module MODULE_PATH = os.path.dirname(inspect.getfile(inspect.currentframe())) @@ -74,19 +74,44 @@ def __init__(self, core, optional=None, additional=None, **kwargs): self.optional = optional self.kwargs = kwargs - def get_version(self, package): - from importlib.metadata import PackageNotFoundError - from importlib.metadata import version as get_lib_version + if os.name == "posix": + self.core.extend(["pexpect"]) + + if self.optional is not None and sys.version_info[1] < 9: + self.optional.append("ansys_corba") + + # Information about the GPU - bare except in case there is a rendering + # bug that the user is trying to report. + if self.kwargs.get("gpu", False) and _HAS_PYVISTA: + from pyvista.utilities.errors import GPUInfo - package = package.replace(".", "-") + try: + self.kwargs["extra_meta"] = [(t[1], t[0]) for t in GPUInfo().get_info()] + except RuntimeError as e: # pragma: no cover + self.kwargs["extra_meta"] = ("GPU Details", f"Error: {str(e)}") + else: + self.kwargs["extra_meta"] = ("GPU Details", "None") + + def get_version(self, package): + try: + import importlib.metadata as importlib_metadata + except ModuleNotFoundError: # pragma: no cover + import importlib_metadata try: - return get_lib_version(package) - except PackageNotFoundError: + return importlib_metadata.version(package.replace(".", "-")) + except importlib_metadata.PackageNotFoundError: return "Package not found" def __repr__(self): - header = ["\n", "Packages Requirements", "*********************"] + header = [ + "-" * 79, + "\n", + "PyMAPDL Software and Environment Report", + "\n", + "Packages Requirements", + "*********************", + ] core = ["\nCore packages", "-------------"] core.extend( @@ -121,11 +146,46 @@ def __repr__(self): else: additional = [""] - return "\n".join(header + core + optional + additional) + return "\n".join(header + core + optional + additional) + self.mapdl_info() + + def mapdl_info(self): + """Return information regarding the ansys environment and installation.""" + # this is here to avoid circular imports + from ansys.mapdl.core.launcher import _get_available_base_ansys + + # List installed Ansys + lines = ["", "Ansys Environment Report", "-" * 79] + lines = ["\n", "Ansys Installation", "******************"] + mapdl_install = _get_available_base_ansys() + if not mapdl_install: + lines.append("Unable to locate any Ansys installations") + else: + lines.append("Version Location") + lines.append("------------------") + for key in sorted(mapdl_install.keys()): + lines.append(f"{key} {mapdl_install[key]}") + install_info = "\n".join(lines) + + env_info_lines = [ + "\n\n\nAnsys Environment Variables", + "***************************", + ] + n_var = 0 + for key, value in os.environ.items(): + if "AWP" in key or "CADOE" in key or "ANSYS" in key: + env_info_lines.append(f"{key:<30} {value}") + n_var += 1 + if not n_var: + env_info_lines.append("None") + env_info = "\n".join(env_info_lines) + return install_info + env_info -if _HAS_SCOOBY: - base_report_class = scooby.Report + +# Determine which type of report will be used (depending on the +# available packages) +if _HAS_PYANSYS_REPORT: + base_report_class = pyansys_report.Report else: # pragma: no cover base_report_class = Plain_Report @@ -133,7 +193,16 @@ def __repr__(self): class Report(base_report_class): """A class for custom scooby.Report.""" - def __init__(self, additional=None, ncol=3, text_width=80, sort=False, gpu=True): + def __init__( + self, + additional=None, + ncol=3, + text_width=80, + sort=False, + gpu=True, + ansys_vars=None, + ansys_libs=None, + ): """Generate a :class:`scooby.Report` instance. Parameters @@ -156,6 +225,16 @@ def __init__(self, additional=None, ncol=3, text_width=80, sort=False, gpu=True) experiencing rendering issues, pass ``False`` to safely generate a report. + ansys_vars : list of str, optional + List containing the Ansys environment variables to be reported. + (e.g. ["MYVAR_1", "MYVAR_2" ...]). Defaults to ``None``. Only used for + the `pyansys-tools-report` package. + + ansys_libs : dict {str : str}, optional + Dictionary containing the Ansys libraries and versions to be reported. + (e.g. {"MyLib" : "v1.2", ...}). Defaults to ``None``. Only used for + the `pyansys-tools-report` package. + """ # Mandatory packages core = [ @@ -169,74 +248,36 @@ def __init__(self, additional=None, ncol=3, text_width=80, sort=False, gpu=True) "google.protobuf", # protobuf library ] - if os.name == "linux": - core.extend(["pexpect"]) - # Optional packages optional = ["matplotlib", "pyvista", "pyiges", "tqdm"] - if sys.version_info[1] < 9: - optional.append("ansys_corba") - - # Information about the GPU - bare except in case there is a rendering - # bug that the user is trying to report. - if gpu and _HAS_PYVISTA: - from pyvista.utilities.errors import GPUInfo - try: - extra_meta = [(t[1], t[0]) for t in GPUInfo().get_info()] - except Exception as e: # pragma: no cover - extra_meta = ("GPU Details", f"Error: {e.message}") - else: - extra_meta = ("GPU Details", "None") - - super().__init__( - additional=additional, - core=core, - optional=optional, - ncol=ncol, - text_width=text_width, - sort=sort, - extra_meta=extra_meta, - ) - - def mapdl_info(self): - """Return information regarding the ansys environment and installation.""" - # this is here to avoid circular imports - from ansys.mapdl.core.launcher import _get_available_base_ansys - - # List installed Ansys - lines = ["", "Ansys Environment Report", "-" * 79] - lines = ["\n", "Ansys Installation", "******************"] - mapdl_install = _get_available_base_ansys() - if not mapdl_install: - lines.append("Unable to locate any Ansys installations") + if _HAS_PYANSYS_REPORT: + # Combine all packages into one + all_mapdl_packages = core + optional + if additional is not None: + all_mapdl_packages += additional + + # Call the pyansys_report.Report constructor + super().__init__( + additional=all_mapdl_packages, + ncol=ncol, + text_width=text_width, + sort=sort, + gpu=gpu, + ansys_vars=ansys_vars, + ansys_libs=ansys_libs, + ) else: - lines.append("Version Location") - lines.append("------------------") - for key in sorted(mapdl_install.keys()): - lines.append(f"{key} {mapdl_install[key]}") - install_info = "\n".join(lines) - - env_info_lines = [ - "\n\n\nAnsys Environment Variables", - "***************************", - ] - n_var = 0 - for key, value in os.environ.items(): - if "AWP" in key or "CADOE" in key or "ANSYS" in key: - env_info_lines.append(f"{key:<30} {value}") - n_var += 1 - if not n_var: - env_info_lines.append("None") - env_info = "\n".join(env_info_lines) - - return install_info + env_info - - def __repr__(self): - add_text = "-" * 79 + "\nPyMAPDL Software and Environment Report" - - report = add_text + super().__repr__() + self.mapdl_info() - return report.replace("-" * 80, "-" * 79) # hotfix for scooby + # Call the PlainReport constructor + super().__init__( + additional=additional, + core=core, + optional=optional, + ncol=ncol, + text_width=text_width, + sort=sort, + gpu=gpu, + ) def is_float(input_string): diff --git a/tests/test_misc.py b/tests/test_misc.py index 80d811d7b0..e23eb27d14 100644 --- a/tests/test_misc.py +++ b/tests/test_misc.py @@ -21,8 +21,14 @@ def test_report(): - report = pymapdl.Report(gpu=system_supports_plotting()) - assert "PyMAPDL Software and Environment Report" in str(report) + report = pymapdl.Report( + additional=["matplotlib", "pyvista", "pyiges", "tqdm"], + gpu=system_supports_plotting(), + ) + assert "PyAnsys Software and Environment Report" in str(report) + + # Check that when adding additional (repeated) packages, they appear only once + assert str(report).count("pyvista") == 1 @pytest.mark.parametrize( @@ -243,7 +249,7 @@ def test_plain_report(): optional = ["pyvista", "tqdm"] additional = ["scipy", "ger"] - report = Plain_Report(core=core, optional=optional, additional=additional) + report = Plain_Report(core=core, optional=optional, additional=additional, gpu=True) rep_str = report.__repr__() for each in core + optional + additional: @@ -261,6 +267,9 @@ def test_plain_report(): assert "Optional packages" in rep_str assert "Additional packages" in rep_str + # Plain report should not represent GPU details evenif asked for + assert "GPU Details" not in rep_str + def test_plain_report_no_options(): from ansys.mapdl.core.misc import Plain_Report