#
# Copyright (C) 2017, 2018, 2019 Smithsonian Astrophysical Observatory
#
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
from distutils.version import LooseVersion
from . import _xspec
__all__ = ['ModelMeta', 'include_if', 'version_at_least']
XSPEC_VERSION = LooseVersion(_xspec.get_xsversion())
def equal_or_greater_than(version_string):
"""
Utility function that compares a version string with the version of the current xspec instance.
For better or worse the xspec current instance is not cached across calls. It probably could be but
it just seems safer not to, and any overhead insists on models initialization only.
The comparison is made in terms of the `distutils.version.LooseVersion` class.
:param version_string: the version against which to compare the current xspec version
:return: `True` if the version of xspec is equal or greater than the argument, `False` otherwise
"""
return XSPEC_VERSION >= LooseVersion(version_string)
[docs]class include_if():
"""
Generic decorator for including xspec models conditionally. It takes a boolean condition as an argument.
If the boolean condition is not met, then the model is not included, and its function is replaced with a
dummy function that throws an exception.
If the model is disabled, then its class's `version_enabled` attribute is set to `False`.
"""
DISABLED_MODEL_MESSAGE = "Model {} is disabled because of an unmet condition"
def __init__(self, condition):
self.condition = condition
def __call__(self, model_class):
if not self.condition:
model_class.version_enabled = False
model_class._calc = self._disabled(self.get_message(model_class))
return model_class
[docs] def get_message(self, model_class):
return self.DISABLED_MODEL_MESSAGE.format(model_class.__name__)
@staticmethod
def _disabled(message):
def wrapped(*args, **kwargs):
raise AttributeError(message)
return wrapped
[docs]class version_at_least(include_if):
"""
Decorator which takes a version string as an argument and enables a model only if
the xspec version detected at runtime is equal or greater than the one provided to the decorator.
"""
DISABLED_MODEL_MESSAGE = "Model {} is disabled because XSPEC version >= {} is required"
def __init__(self, version_string):
include_if.__init__(self, equal_or_greater_than(version_string))
self.version_string = version_string
[docs] def get_message(self, model_class):
return self.DISABLED_MODEL_MESSAGE.format(model_class.__name__, self.version_string)