"""
A simple class to manage a piece of global science state. See
:ref:`config-developer` for more details.
"""
from contextlib import contextmanager
import warnings
from ..config import ConfigItem
from .exceptions import AstropyDeprecationWarning
from ..utils import find_current_module
__all__ = ['ScienceState', 'ScienceStateAlias']
[docs]class ScienceState(object):
"""
Science state subclasses are used to manage global items that can
affect science results. Subclasses will generally override
`validate` to convert from any of the acceptable inputs (such as
strings) to the appropriate internal objects, and set an initial
value to the ``_value`` member so it has a default.
Examples
--------
::
class MyState(ScienceState):
@classmethod
def validate(cls, value):
if value not in ('A', 'B', 'C'):
raise ValueError("Must be one of A, B, C")
return value
"""
def __init__(self):
raise RuntimeError(
"This class is a singleton. Do not instantiate.")
@classmethod
[docs] def get(cls):
"""
Get the current science state value.
"""
return cls.validate(cls._value)
@classmethod
[docs] def set(cls, value):
"""
Set the current science state value.
"""
class _Context(object):
def __init__(self, parent, value):
self._value = value
self._parent = parent
def __enter__(self):
pass
def __exit__(self, type, value, tb):
self._parent._value = self._value
ctx = _Context(cls, cls._value)
value = cls.validate(value)
cls._value = value
return ctx
@classmethod
[docs] def validate(cls, value):
"""
Validate the value and convert it to its native type, if
necessary.
"""
return value
[docs]class ScienceStateAlias(ConfigItem):
"""
This is a backward compatibility layer for configuration items
that moved to ScienceState classes in astropy 0.4.
Parameters
----------
since : str
The version in which the configuration item was converted into
science state.
python_name : str
The old name of the Python variable for the configuration item.
config_name : str
The old name of the configuration item in the configuration file.
science_state : ScienceState subclass
The science state class that now manages this information.
cfgtype : str or `None`, optional
A type specifier like those used as the *values* of a particular key
in a ``configspec`` file of ``configobj``. If `None`, the type will
be inferred from the default value.
module : str, optional
The module containing the old configuration item.
"""
# REMOVE in astropy 0.5
def __init__(self, since, python_name, config_name, science_state,
cfgtype=None, module=None):
# We have to do the automatic module determination here, not
# just in ConfigItem, otherwise the extra stack frame will
# make it come up with the wrong answer.
if module is None:
module = find_current_module(2)
if module is None:
msg1 = 'Cannot automatically determine get_config module, '
msg2 = 'because it is not called from inside a valid module'
raise RuntimeError(msg1 + msg2)
else:
module = module.__name__
self._dont_warn = True
self._since = since
self._python_name = python_name
self._config_name = config_name
self._science_state = science_state
super(ScienceStateAlias, self).__init__(
science_state._value,
science_state.__doc__,
cfgtype=cfgtype,
module=module)
self.name = config_name
# Set the default value of the science state from the config
# file, if defined. This is what pulls any old values in the
# config file and applies them to the science state.
value = super(ScienceStateAlias, self).__call__()
# We got a value in the config file
if science_state.validate(value) != science_state._value:
warnings.warn(
"Config parameter '{0}' in section [{1}] is deprecated. "
"Use science state {2}.{3} instead.".format(
self.name, self.module, self._science_state.__module__,
self._science_state.__name__),
AstropyDeprecationWarning)
del self._dont_warn
def _deprecation_warning(self, extra=None):
if hasattr(self, '_dont_warn'):
return
message = ("'{0}.{1}' is deprecated, and is no longer defined "
"as a configuration item.".format(
self.module, self._python_name))
if extra is not None:
message += " Use '{0}.{1}{2}' instead.".format(
self._science_state.__module__,
self._science_state.__name__,
extra)
warnings.warn(message, AstropyDeprecationWarning)
[docs] def set(self, value):
self._deprecation_warning('.set()')
self._science_state.set(value)
@contextmanager
[docs] def set_temp(self, value):
self._deprecation_warning('.set_temp()')
with self._science_state.set(value):
yield
[docs] def reload(self):
self._deprecation_warning()
self._dont_warn = True
try:
result = super(ScienceStateAlias, self).reload()
finally:
del self._dont_warn
if result is not None:
self._science_state.set(result)
[docs] def __call__(self):
self._deprecation_warning('.get()')
return self._science_state.get()