From 957e4728f06b3ceeea6a9882c3bd5609ea39c67f Mon Sep 17 00:00:00 2001 From: Jonathan Guyer Date: Mon, 22 Jan 2024 16:26:31 -0500 Subject: [PATCH] Log much more information about FiPy environment (#990) Remove obsolete vitals module (svn, really???) --- fipy/__init__.py | 26 +++++- fipy/tests/test.py | 2 +- fipy/tools/__init__.py | 2 - fipy/tools/logging/__init__.py | 60 ------------- fipy/tools/logging/environment.py | 139 ++++++++++++++++++++++++++++++ fipy/tools/vitals.py | 139 ------------------------------ 6 files changed, 163 insertions(+), 205 deletions(-) create mode 100644 fipy/tools/logging/environment.py delete mode 100644 fipy/tools/vitals.py diff --git a/fipy/__init__.py b/fipy/__init__.py index c3e5379845..3ec0ed179e 100644 --- a/fipy/__init__.py +++ b/fipy/__init__.py @@ -64,9 +64,29 @@ def _excepthook(*args): __version__ = get_versions()['version'] del get_versions -from fipy.tools.logging import package_info -_log.info(package_info()) -del package_info + +from fipy.tools.logging import environment + +_fipy_environment = { + "argv": sys.argv, + "environ": dict(os.environ), + "platform": environment.platform_info(), + "package": environment.package_info() +} + +if _log.isEnabledFor(logging.DEBUG): + try: + _fipy_environment["conda"] = environment.conda_info() + except: + pass + + try: + _fipy_environment["pip"] = environment.pip_info() + except: + pass + +_log.debug(json.dumps(_fipy_environment)) + from fipy.boundaryConditions import * from fipy.meshes import * diff --git a/fipy/tests/test.py b/fipy/tests/test.py index 709e95eaa0..a605cd7f30 100644 --- a/fipy/tests/test.py +++ b/fipy/tests/test.py @@ -161,7 +161,7 @@ def _test_args(self): yield "examples.test._suite" def printPackageInfo(self): - from fipy.tools.logging import package_info + from fipy.tools.logging.environment import package_info packages = package_info() diff --git a/fipy/tools/__init__.py b/fipy/tools/__init__.py index 89e6d65e72..7895c7706d 100644 --- a/fipy/tools/__init__.py +++ b/fipy/tools/__init__.py @@ -57,7 +57,6 @@ from fipy.tools import vector from .dimensions.physicalField import PhysicalField from fipy.tools.numerix import * -from fipy.tools.vitals import Vitals from fipy.tools.sharedtempfile import SharedTemporaryFile __all__ = ["serialComm", @@ -66,7 +65,6 @@ "numerix", "vector", "PhysicalField", - "Vitals", "serial", "parallel", "SharedTemporaryFile"] diff --git a/fipy/tools/logging/__init__.py b/fipy/tools/logging/__init__.py index 4b959d7e85..e69de29bb2 100644 --- a/fipy/tools/logging/__init__.py +++ b/fipy/tools/logging/__init__.py @@ -1,60 +0,0 @@ -import sys - -def package_info(): - """Return dictionary of versions for FiPy dependencies.""" - - packages = {} - - packages['python'] = sys.version.replace('\n', '| ') - - for pkg in ['fipy', 'numpy', 'pysparse', 'scipy', 'matplotlib', 'mpi4py', 'petsc4py', 'pyamgx']: - try: - mod = __import__(pkg) - - packages[pkg] = mod.__version__ - except ImportError as e: - packages[pkg] = 'not installed' - - except Exception as e: - packages[pkg] = 'version check failed: {}'.format(e) - - ## PyTrilinos - try: - import PyTrilinos - packages['PyTrilinos'] = PyTrilinos.version() - except ImportError as e: - packages['PyTrilinos'] = 'not installed' - except Exception as e: - packages['PyTrilinos'] = 'version check failed: {}'.format(e) - - ## Mayavi uses a non-standard approach for storing its version number. - try: - from mayavi.__version__ import __version__ as mayaviversion - packages['mayavi'] = mayaviversion - except ImportError as e: - try: - from enthought.mayavi.__version__ import __version__ as mayaviversion - packages['mayavi'] = mayaviversion - except ImportError as e: - packages['mayavi'] = 'not installed' - except Exception as e: - packages['mayavi'] = 'version check failed: {}'.format(e) - - ## Gmsh version - try: - from fipy.meshes.gmshMesh import gmshVersion - gmshversion = gmshVersion() - if gmshversion is None: - packages['gmsh'] = 'not installed' - else: - packages['gmsh'] = gmshversion - except Exception as e: - packages['gmsh'] = 'version check failed: {}'.format(e) - - try: - from fipy.solvers import solver_suite - packages['solver'] = solver_suite - except Exception as e: - packages['solver'] = str(e) - - return packages diff --git a/fipy/tools/logging/environment.py b/fipy/tools/logging/environment.py new file mode 100644 index 0000000000..7a872a4601 --- /dev/null +++ b/fipy/tools/logging/environment.py @@ -0,0 +1,139 @@ +import json +import platform +import subprocess +import sys + +__all__ = ["conda_info", "pip_info", "package_info", "platform_info"] + +def conda_info(conda="conda"): + """Collect information about conda environment. + + Parameters + ---------- + conda : str + Name of conda executable (default: "conda"). + + Returns + ------- + dict + Result of `conda info` and `conda env export` for active conda + environment. + """ + info = {} + p = subprocess.Popen([conda, "info", "--json"], stdout=subprocess.PIPE) + stdout, _ = p.communicate() + stdout = stdout.decode('ascii') + + info["conda_info"] = json.loads(stdout) + p = subprocess.Popen([conda, "env", "export", + "--name", info["conda_info"]["active_prefix_name"], + "--json"], + stdout=subprocess.PIPE) + stdout, _ = p.communicate() + stdout = stdout.decode('ascii') + info["conda_env"] = json.loads(stdout) + + return info + +def pip_info(python="python"): + """Collect information about pip environment. + + Parameters + ---------- + python : str + Name of Python executable (default: "python"). + + Returns + ------- + list of dict + Result of `pip list --format json`. + """ + info = {} + p = subprocess.Popen([python, "-m", "pip", "list", "--format", "json"], stdout=subprocess.PIPE) + stdout, _ = p.communicate() + stdout = stdout.decode('ascii') + + return json.loads(stdout) + +def package_info(): + """Collect information about installed packages FiPy uses. + + Returns + ------- + dict + Versions of important Python packages. + """ + packages = {} + + packages['python'] = sys.version #.replace('\n', '| ') + + for pkg in ['fipy', 'numpy', 'pysparse', 'scipy', 'matplotlib', 'mpi4py', 'petsc4py', 'pyamgx']: + try: + mod = __import__(pkg) + + packages[pkg] = mod.__version__ + except ImportError as e: + packages[pkg] = 'not installed' + + except Exception as e: + packages[pkg] = 'version check failed: {}'.format(e) + + ## PyTrilinos + try: + import PyTrilinos + packages['PyTrilinos'] = PyTrilinos.version() + except ImportError as e: + packages['PyTrilinos'] = 'not installed' + except Exception as e: + packages['PyTrilinos'] = 'version check failed: {}'.format(e) + + ## Mayavi uses a non-standard approach for storing its version number. + try: + from mayavi.__version__ import __version__ as mayaviversion + packages['mayavi'] = mayaviversion + except ImportError as e: + try: + from enthought.mayavi.__version__ import __version__ as mayaviversion + packages['mayavi'] = mayaviversion + except ImportError as e: + packages['mayavi'] = 'not installed' + except Exception as e: + packages['mayavi'] = 'version check failed: {}'.format(e) + + ## Gmsh version + try: + from fipy.meshes.gmshMesh import gmshVersion + gmshversion = gmshVersion() + if gmshversion is None: + packages['gmsh'] = 'not installed' + else: + packages['gmsh'] = gmshversion + except Exception as e: + packages['gmsh'] = 'version check failed: {}'.format(e) + + try: + from fipy.solvers import solver_suite + packages['solver'] = solver_suite + except Exception as e: + packages['solver'] = str(e) + + return packages + +def platform_info(): + """Collect information about platform Python is running in. + + Returns + ------- + dict + Data extracted from `platform` package. + """ + return { + "architecture": platform.architecture(), + "machine": platform.machine(), + "node": platform.node(), + "platform": platform.platform(), + "processor": platform.processor(), + "release": platform.release(), + "system": platform.system(), + "version": platform.version() + } diff --git a/fipy/tools/vitals.py b/fipy/tools/vitals.py deleted file mode 100644 index be62b50c78..0000000000 --- a/fipy/tools/vitals.py +++ /dev/null @@ -1,139 +0,0 @@ -from __future__ import print_function -from __future__ import unicode_literals -from builtins import zip -from builtins import str -import os -import platform -import subprocess -import sys -from xml.dom.minidom import Document - -import fipy - -__all__ = ["Vitals"] -from future.utils import text_to_native_str -__all__ = [text_to_native_str(n) for n in __all__] - -class Vitals(Document): - """Returns XML formatted information about current FiPy environment - """ - - def __init__(self): - Document.__init__(self) - - self.top = self.createElementNS("http://www.ctcms.nist.gov/fipy", "FiPy") - Document.appendChild(self, self.top) - - self.appendChild(self.tupleToXML(sys.argv, "sys.argv")) - - version = self.createElement("version") - self.appendChild(version) - version.appendChild(self.createTextNode(fipy.__version__)) - - path = self.createElement("path") - fipypath = os.path.dirname(fipy.__file__) - path.appendChild(self.createTextNode(fipypath)) - self.appendChild(path) - - self.appendChild(self.svn(fipypath)) - - self.appendChild(self.dictToXML(os.environ, "environ")) - - pyth = self.createElement("python") - - implementation = self.createElement("implementation") - if hasattr(platform, "python_implementation"): - implementation.appendChild(self.createTextNode(platform.python_implementation())) - else: - implementation.appendChild(self.createTextNode("unknown")) - pyth.appendChild(implementation) - - pversion = self.createElement("version") - pversion.appendChild(self.createTextNode(platform.python_version())) - pyth.appendChild(pversion) - - compiler = self.createElement("compiler") - compiler.appendChild(self.createTextNode(platform.python_compiler())) - pyth.appendChild(compiler) - - pyth.appendChild(self.tupleToXML(platform.python_build(), "build", - keys=("buildno", "builddate"))) - - pyth.appendChild(self.tupleToXML(platform.architecture(), "architecture", - keys=("bits", "linkage"))) - - pyth.appendChild(self.tupleToXML(platform.uname(), "uname", - keys=("system", "node", "release", "version", "machine", "processor"))) - - self.appendChild(pyth) - - def appendChild(self, child): - self.top.appendChild(child) - - def dictToXML(self, d, name): - elem = self.createElement(name) - for key, value in list(d.items()): - keyelem = self.createElement(key) - keyelem.appendChild(self.createTextNode(str(value))) - elem.appendChild(keyelem) - - return elem - - def tupleToXML(self, t, name, keys=None): - elem = self.createElement(name) - if keys is not None: - for key, value in zip(keys, t): - keyelem = self.createElement(key) - keyelem.appendChild(self.createTextNode(str(value))) - elem.appendChild(keyelem) - else: - for value in t: - elem.appendChild(self.createTextNode(str(value))) - - return elem - - def svncmd(self, cmd, *args): - elem = self.createElement(cmd) - p = subprocess.Popen(["svn", cmd] + list(args), stdout=subprocess.PIPE) - info = p.communicate()[0] - if p.returncode == 0: - elem.appendChild(self.createTextNode(info)) - else: - raise OSError - - return elem - - def svn(self, *args): - elem = self.createElement("svn") - for cmd in ["info", "status", "diff"]: - try: - elem.appendChild(self.svncmd(cmd, *args)) - except: - pass - - return elem - - def __str__(self): - return self.toprettyxml() - - def save(self, fname): - f = open(fname, 'w') - self.writexml(f, indent=" ", addindent=" ", newl="\n") - f.close() - - def appendInfo(self, name, svnpath=None, **kwargs): - """append some additional information, possibly about a project under a separate svn repository - """ - elem = self.dictToXML(kwargs, name) - if svnpath is not None: - elem.appendChild(self.svn(svnpath)) - self.appendChild(elem) - -if __name__ == "__main__": - v = Vitals() - - solar = v.createElement("solar") - solar.appendChild(v.svn("/Users/guyer/Documents/research/codes/solar-dimensionless")) - v.appendChild(solar) - - print(v)