Let gmxapi.commandline access GROMACS installation details.
authorM. Eric Irrgang <mei2n@virginia.edu>
Thu, 24 Jun 2021 14:15:29 +0000 (14:15 +0000)
committerPaul Bauer <paul.bauer.q@gmail.com>
Thu, 24 Jun 2021 14:15:29 +0000 (14:15 +0000)
python_packaging/src/gmxapi/commandline.py
python_packaging/src/gmxapi/testsupport.py

index 04269854925ccc7f6e5fa2f982975f64f416b432..b6351b7486c59ada5d9e36cd7a6bcc03fcb61a9e 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2019,2020, by the GROMACS development team, led by
+# Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -38,7 +38,9 @@ Provide command line operation.
 
 __all__ = ['commandline_operation']
 
+import functools
 import os
+import pathlib
 import shutil
 import subprocess
 
@@ -53,6 +55,42 @@ logger = root_logger.getChild('commandline')
 logger.info('Importing {}'.format(__name__))
 
 
+@functools.lru_cache()
+def _config() -> dict:
+    """Get the GROMACS configuration detected during installation.
+
+    If this appears to be a useful function, it may become part of the regular
+    interface, but it is currently unadvertised.
+    """
+    import json
+    from importlib.resources import open_text
+    with open_text('gmxapi', 'gmxconfig.json') as textfile:
+        config = json.load(textfile)
+    return config
+
+
+@functools.lru_cache()
+def cli_executable() -> pathlib.Path:
+    """Report the installed GROMACS command line executable."""
+    path = _config().get('gmx_executable', None)
+    if path is not None:
+        path = pathlib.Path(os.path.abspath(path))
+        if path.is_file():
+            return path
+    raise exceptions.FeatureNotAvailableError('GROMACS installation unavailable.')
+
+
+@functools.lru_cache()
+def cli_bindir() -> pathlib.Path:
+    """Report the installed GROMACS binary directory."""
+    path = _config().get('gmx_bindir', None)
+    if path is not None:
+        path = pathlib.Path(os.path.abspath(path))
+        if path.is_dir():
+            return path
+    raise exceptions.FeatureNotAvailableError('GROMACS installation unavailable.')
+
+
 # Create an Operation that consumes a list and a boolean to produce a string and an integer.
 #
 # Wrap the defined function using a decorator that
index 0ffdf02435a072d40ab230577a18cc289205fb94..bc802297f2d1b54fc8c233b07bb1ec2f5ca537c9 100644 (file)
@@ -1,7 +1,7 @@
 #
 # This file is part of the GROMACS molecular simulation package.
 #
-# Copyright (c) 2019,2020, by the GROMACS development team, led by
+# Copyright (c) 2019,2020,2021, by the GROMACS development team, led by
 # Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
 # and including many others, as listed in the AUTHORS file in the
 # top-level source directory and at http://www.gromacs.org.
@@ -118,27 +118,14 @@ def remove_tempdir(request) -> RmOption:
     arg = request.config.getoption('--rm')
     return RmOption(arg)
 
+
 @pytest.fixture(scope='session')
 def gmxconfig():
-    try:
-        from importlib.resources import open_text
-        with open_text('gmxapi', 'gmxconfig.json') as textfile:
-            config = json.load(textfile)
-    except ImportError:
-        # TODO: Remove this when we require Python 3.7
-        try:
-            # A backport of importlib.resources is available as importlib_resources
-            # with a somewhat different interface.
-            from importlib_resources import files, as_file
-
-            source = files('gmxapi').joinpath('gmxconfig.json')
-            with as_file(source) as gmxconfig:
-                with open(gmxconfig, 'r') as fp:
-                    config = json.load(fp)
-        except ImportError:
-            config = None
+    from .commandline import _config
+    config = _config()
     yield config
 
+
 @pytest.fixture(scope='session')
 def mdrun_kwargs(request, gmxconfig):
     """pytest fixture to provide a mdrun_kwargs dictionary for the mdrun ResourceManager.
@@ -255,50 +242,11 @@ def cleandir(remove_tempdir: RmOption):
         yield newdir
 
 
-class GmxBin:
-    """Represent the detected GROMACS installation."""
-    def __init__(self, gmxconfig):
-        # Try to use package resources to locate the "gmx" binary wrapper.
-        if gmxconfig is not None:
-            gmxbindir = gmxconfig.get('gmx_bindir', None)
-            command = gmxconfig.get('gmx_executable', None)
-        else:
-            gmxbindir = None
-            command = None
-
-        # TODO: Remove fall-back when we can rely on gmxconfig.json via importlib.resources in Py 3.7+.
-        allowed_command_names = ['gmx', 'gmx_mpi']
-        for command_name in allowed_command_names:
-            if command is not None:
-                break
-            command = shutil.which(command_name)
-            if command is None:
-                gmxbindir = os.getenv('GMXBIN')
-                if gmxbindir is None:
-                    gromacsdir = os.getenv('GROMACS_DIR')
-                    if gromacsdir is not None and gromacsdir != '':
-                        gmxbindir = os.path.join(gromacsdir, 'bin')
-                if gmxbindir is None:
-                    gmxapidir = os.getenv('gmxapi_DIR')
-                    if gmxapidir is not None and gmxapidir != '':
-                        gmxbindir = os.path.join(gmxapidir, 'bin')
-                if gmxbindir is not None:
-                    gmxbindir = os.path.abspath(gmxbindir)
-                    command = shutil.which(command_name, path=gmxbindir)
-
-        self._command = command
-        self._bindir = gmxbindir
-
-    def command(self):
-        return self._command
-
-    def bindir(self):
-        return self._bindir
-
-
 @pytest.fixture(scope='session')
-def gmxcli(gmxconfig):
-    command = GmxBin(gmxconfig).command()
+def gmxcli():
+    from .commandline import cli_executable
+    command = cli_executable()
+
     if command is None:
         message = "Tests need 'gmx' command line tool, but could not find it on the path."
         raise RuntimeError(message)