1# 2# This file is part of the GROMACS molecular simulation package. 3# 4# Copyright (c) 2019,2020,2021, by the GROMACS development team, led by 5# Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl, 6# and including many others, as listed in the AUTHORS file in the 7# top-level source directory and at http://www.gromacs.org. 8# 9# GROMACS is free software; you can redistribute it and/or 10# modify it under the terms of the GNU Lesser General Public License 11# as published by the Free Software Foundation; either version 2.1 12# of the License, or (at your option) any later version. 13# 14# GROMACS is distributed in the hope that it will be useful, 15# but WITHOUT ANY WARRANTY; without even the implied warranty of 16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17# Lesser General Public License for more details. 18# 19# You should have received a copy of the GNU Lesser General Public 20# License along with GROMACS; if not, see 21# http://www.gnu.org/licenses, or write to the Free Software Foundation, 22# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 23# 24# If you want to redistribute modifications to GROMACS, please 25# consider that scientific software is very special. Version 26# control is crucial - bugs must be traceable. We will be happy to 27# consider code for inclusion in the official distribution, but 28# derived work must not be called official GROMACS. Details are found 29# in the README & COPYING files - if they are missing, get the 30# official version at http://www.gromacs.org. 31# 32# To help us fund GROMACS development, we humbly ask that you cite 33# the research papers on the package. Check out http://www.gromacs.org. 34 35""" 36gmxapi version and release information. 37 38The ``gmxapi.__version__`` attribute contains a :pep:`version string <440>`. 39The more general way to access the package version is with the 40:py:mod:`pkg_resources <https://setuptools.readthedocs.io/en/latest/pkg_resources.html>` module:: 41 42 pkg_resources.get_distribution('gmxapi').version 43 44`gmxapi.version` module functions `api_is_at_least()` and `has_feature()` 45support additional convenience and introspection. 46 47.. versionchanged:: 0.2 48 49 This module no longer provides public data attributes. 50 Instead, use the module functions or 51 :py:mod:`packaging.version <https://packaging.pypa.io/en/latest/version/>`. 52 53.. seealso:: 54 55 Consider https://packaging.pypa.io/en/latest/version/ for programmatic 56 handling of the version string. For example:: 57 58 from packaging.version import parse 59 gmxapi_version = pkg_resources.get_distribution('gmxapi').version 60 if parse(gmxapi_version).is_prerelease: 61 print('The early bird gets the worm.') 62 63.. todo:: Use pkg_resources.get_distribution('gmxapi').version and 64 "development installations" instead of relying on or publicizing 65 a __version__ attribute. 66""" 67import warnings 68 69from .exceptions import FeatureNotAvailableError 70 71# TODO: Version management policy and procedures. 72_major = 0 73_minor = 2 74_micro = 3 75_suffix = '' 76 77# Reference https://www.python.org/dev/peps/pep-0440/ 78# and https://packaging.pypa.io/en/latest/version/ 79__version__ = '{major}.{minor}.{micro}{suffix}'.format(major=_major, 80 minor=_minor, 81 micro=_micro, 82 suffix=_suffix) 83 84# Features added since the initial gmxapi prototype, targeted for version 0.1. 85_named_features_0_0 = ['fr1', 'fr3', 'fr7', 'fr15'] 86# Features named since the finalization of the 0.1 specification with GROMACS 2020. 87_named_features_0_1 = [] 88# Named features describe functionality or behavior introduced since the last 89# major release, and should be described in gmxapi documentation or issue 90# tracking system. Note that, as features become part of the specification, 91# conditionals depending on them should be phased out of the package source. At 92# major releases, the named feature list should be reset to empty. Optionally, 93# we could raise a DeprecationWarning for calls to has_feature() for features 94# that have become part of the specification, at least for a few minor release or 95# a few years, to avoid introducing errors to client code. 96# 97# Bugs and bug fixes may be indicated with names consisting of tracked issue URLs. 98# 99# Features consisting of 'fr' and a numeric suffix are the functional requirements 100# described in roadmap.rst, as described at https://gitlab.com/gromacs/gromacs/-/issues/2893 101# 102# fr1: wrap importable Python code. 103# fr2: output proxy establishes execution dependency (superseded by fr3) 104# fr3: output proxy can be used as input 105# fr4: dimensionality and typing of named data causes generation of correct work topologies 106# fr5: explicit many-to-one or many-to-many data flow 107# fr7: Python bindings for launching simulations 108# fr8: gmx.mdrun understands ensemble work 109# fr9: MD plugins 110# fr10: fused operations for use in looping constructs 111# fr11: Python access to TPR file contents 112# fr12: Simulation checkpoint handling 113# fr13: ``run`` module function simplifies user experience 114# fr14: Easy access to GROMACS run time parameters 115# fr15: Simulation input modification 116# fr16: Create simulation input from simulation output 117# fr17: Prepare simulation input from multiple sources 118# fr18: GROMACS CLI tools receive improved Python-level support over generic commandline_operations 119# fr19: GROMACS CLI tools receive improved C++-level support over generic commandline_operations 120# fr20: Python bindings use C++ API for expressing user interface 121# fr21 User insulated from filesystem paths 122# fr22 MPI-based ensemble management from Python 123# fr23 Ensemble simulations can themselves use MPI 124 125 126def api_is_at_least(major_version, minor_version=0, patch_version=0): 127 """Allow client to check whether installed module supports the requested API level. 128 129 Arguments: 130 major_version (int): gmxapi major version number. 131 minor_version (int): optional gmxapi minor version number (default: 0). 132 patch_version (int): optional gmxapi patch level number (default: 0). 133 134 Returns: 135 True if installed gmx package is greater than or equal to the input level 136 137 Note that if gmxapi.version.release is False, the package is not guaranteed to correctly or 138 fully support the reported API level. 139 """ 140 if not isinstance(major_version, int) or not isinstance(minor_version, int) or not isinstance(patch_version, int): 141 raise TypeError('Version levels must be provided as integers.') 142 if _major > major_version: 143 return True 144 elif _major == major_version and _minor >= minor_version: 145 return True 146 elif _major == major_version and _minor == minor_version and _micro >= patch_version: 147 return True 148 else: 149 return False 150 151 152def has_feature(name='', enable_exception=False) -> bool: 153 """Query whether a named feature is available in the installed package. 154 155 Between updates to the API specification, new features or experimental aspects 156 may be introduced into the package and need to be detectable. This function 157 is intended to facilitate code testing and resolving differences between 158 development branches. Users should refer to the documentation for the package 159 modules and API level. 160 161 The primary use case is, in conjunction with `api_is_at_least()`, to allow 162 client code to robustly identify expected behavior and API support through 163 conditional execution and branching. Note that behavior is strongly 164 specified by the API major version number. Features that have become part of 165 the specification and bug-fixes referring to previous major versions should 166 not be checked with *has_feature()*. Using *has_feature()* with old feature 167 names will produce a DeprecationWarning for at least one major version, and 168 client code should be updated to avoid logic errors in future versions. 169 170 For convenience, setting ``enable_exception = True`` causes the function to 171 instead raise a gmxapi.exceptions.FeatureNotAvailableError for unrecognized feature names. 172 This allows extension code to cleanly produce a gmxapi exception instead of 173 first performing a boolean check. Also, some code may be unexecutable for 174 more than one reason, and sometimes it is cleaner to catch all 175 `gmxapi.exceptions.Error` exceptions for a code block, rather than to 176 construct complex conditionals. 177 178 Returns: 179 True if named feature is recognized by the installed package, else False. 180 181 Raises: 182 gmxapi.exceptions.FeatureNotAvailableError: If ``enable_exception == True`` and feature is not found. 183 184 """ 185 # First, issue a warning if the feature name is subject to removal because 186 # of the history of the API specification. 187 if api_is_at_least(0, 2): 188 # For sufficiently advanced API versions, we want to warn that old 189 # feature checks lose meaning and should no longer be checked. 190 # We provide a suggestion with the API version that absorbed their 191 # specification. 192 if name in _named_features_0_0: 193 warnings.warn( 194 'Old feature name. Use `api_is_at_least(0, 1)` instead of `has_feature({})`.'.format(name), 195 category=DeprecationWarning, 196 stacklevel=2 197 ) 198 199 # Check whether the feature is listed in the API specification amendments. 200 if name in _named_features_0_0 + _named_features_0_1: 201 return True 202 else: 203 if enable_exception: 204 raise FeatureNotAvailableError('Feature {} not available.'.format(str(name))) 205 return False 206