Skip to content

Implement pyansys-tools-report in PyMAPDL #1215

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 18 commits into from
Jun 28, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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'",
Expand Down
1 change: 1 addition & 0 deletions requirements/requirements_tests.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
199 changes: 120 additions & 79 deletions src/ansys/mapdl/core/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()))
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -121,19 +146,63 @@ 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


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
Expand All @@ -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 = [
Expand All @@ -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):
Expand Down
15 changes: 12 additions & 3 deletions tests/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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:
Expand All @@ -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
Expand Down