<commit_before># coding: utf-8
"""
Base classes for Variable objects.

"""
import itertools
from collections import Iterable, OrderedDict

from xarray.core.variable import as_variable


class ValidationError(ValueError):
    """An error while validating data."""
    pass


class AbstractVariable(object):
    """Abstract class for all variables.

    This class aims at providing a common parent class
    for all regular, diagnostic, foreign and undefined variables.

    """
    def __init__(self, provided=False, description='', attrs=None,
                 group=None):
        self.provided = provided
        self.description = description
        self.attrs = attrs or {}
        self.group = group

    def __repr__(self):
        return "<xsimlab.%s>" % type(self).__name__


class Variable(AbstractVariable):
    """Base class that represents a variable in a process or a model.

    `Variable` objects store useful metadata such as dimension labels,
    a short description, a default value or other user-provided metadata.

    Variables allow to convert any given value to a `xarray.Variable` object
    after having perfomed some checks.

    In processes, variables are instantiated as class attributes. They
    represent fundamental elements of a process interface (see
    :class:`Process`) and by extension a model interface.
    Some attributes such as `provided` and `optional` also contribute to
    the definition of the interface.

    """
    default_validators = []  # Default set of validators

    def __init__(self, allowed_dims, provided=False, optional=False,
                 default_value=None, validators=(), group=None,
                 description='', attrs=None):
        """
        Parameters
        ----------
        allowed_dims : str or tuple or list
            Dimension label(s) allowed for the variable. An empty tuple
            corresponds to a scalar variable, a string or a 1-length tuple
            corresponds to a 1-d variable and a n-length tuple corresponds to a
            n-d variable. A list of str or tuple items may also be provided if
            the variable accepts different numbers of dimensions.
            This should not include a time dimension, which is always allowed.
        provided : bool, optional
            Defines whether a value for the variable is required (False)
            or provided (True) by the process in which it is defined
            (default: False).
            If `provided=True`, then the variable in a process/model won't
            be considered as an input of that process/model.
        optional : bool, optional
            If True, a value may not be required for the variable
            (default: False). Ignored when `provided` is True.
        default_value : object, optional
            Single default value for the variable (default: None). It
            will be automatically broadcasted to all of its dimensions.
            Ignored when `provided` is True.
        validators : tuple or list, optional
            A list of callables that take an `xarray.Variable` object and
            raises a `ValidationError` if it doesn’t meet some criteria.
            It may be useful for custom, advanced validation that
            can be reused for different variables.
        group : str, optional
            Variable group.
        description : str, optional
            Short description of the variable (ideally one-line).
        attrs : dict, optional
            Dictionnary of additional metadata (e.g., standard_name,
            units, math_symbol...).

        """
        super(Variable, self).__init__(provided=provided, group=group,
                                       description=description,
                                       attrs=attrs)

        if not len(allowed_dims):
            allowed_dims = [()]
        if isinstance(allowed_dims, str):
            allowed_dims = [(allowed_dims,)]
        elif isinstance(allowed_dims, list):
            allowed_dims = [tuple([d]) if isinstance(d, str) else tuple(d)
                            for d in allowed_dims]
        elif len(allowed_dims) == 1:
            allowed_dims = [allowed_dims]
        self.allowed_dims = tuple(allowed_dims)

        self.optional = optional
        self.default_value = default_value
        self._validators = list(validators)
        self._state = None
        self._rate = None
        self._change = None

    @property
    def validators(self):
        return list(itertools.chain(self.default_validators, self._validators))

    def run_validators(self, xr_variable):
        for vfunc in self.validators:
            vfunc(xr_variable)

    def validate(self, xr_variable):
        pass

    def validate_dimensions(self, dims, ignore_dims=None):
        """Validate given dimensions for this variable.

        Parameters
        ----------
        dims : tuple
            Dimensions given for the variable.
        ignore_dims : tuple, optional
            Dimensions that are ignored during the validation process.
            Typically, it may correspond to a time dimension (time steps)
            and/or a grid-search or parameter-space dimension for this
            variable itself.

        Raises
        ------
        ValidationError
            If the given dimensions are not valid, i.e., if they doesn't
            correspond to allowed dimensions for this variable.

        """
        dims_dict = OrderedDict([(d, None) for d in dims])

        if ignore_dims is not None:
            for d in ignore_dims:
                dims_dict.pop(d, None)

        clean_dims = tuple(dims_dict)
        test_dims = [d for d in self.allowed_dims if d == clean_dims]

        if not test_dims:
            raise ValidationError("invalid dimensions %s\n"
                                  "allowed dimensions are %s"
                                  % (dims, self.allowed_dims))

    def to_xarray_variable(self, value):
        """Convert the input value to an `xarray.Variable` object.

        Parameters
        ----------
        value : object
            The input value can be in the form of a single value,
            an array-like, a ``(dims, data[, attrs])`` tuple, another
            `xarray.Variable` object or a `xarray.DataArray` object.
            if None, the `default_value` attribute is used to set the value.

        Returns
        -------
        variable : `xarray.Variable`
            A xarray Variable object whith data corresponding to the input (or
            default) value and with attributes ('description' and other
            key:value pairs found in `Variable.attrs`).

        """
        if value is None:
            value = self.default_value

        # in case where value is a 1-d array without dimension name,
        # dimension name is set to 'this_variable' and has to be renamed
        # later by the name of the variable in a process/model.
        xr_variable = as_variable(value, name='this_variable')

        xr_variable.attrs.update(self.attrs)
        xr_variable.attrs['description'] = self.description

        return xr_variable

    @property
    def state(self):
        """State value of the variable, i.e., the instant value at a given
        time or simply the value if the variable is not time dependent.
        """<commit_msg>if absent, description not added as attr of xarray Variable<commit_after># coding: utf-8
"""
Base classes for Variable objects.

"""
import itertools
from collections import Iterable, OrderedDict

from xarray.core.variable import as_variable


class ValidationError(ValueError):
    """An error while validating data."""
    pass


class AbstractVariable(object):
    """Abstract class for all variables.

    This class aims at providing a common parent class
    for all regular, diagnostic, foreign and undefined variables.

    """
    def __init__(self, provided=False, description='', attrs=None,
                 group=None):
        self.provided = provided
        self.description = description
        self.attrs = attrs or {}
        self.group = group

    def __repr__(self):
        return "<xsimlab.%s>" % type(self).__name__


class Variable(AbstractVariable):
    """Base class that represents a variable in a process or a model.

    `Variable` objects store useful metadata such as dimension labels,
    a short description, a default value or other user-provided metadata.

    Variables allow to convert any given value to a `xarray.Variable` object
    after having perfomed some checks.

    In processes, variables are instantiated as class attributes. They
    represent fundamental elements of a process interface (see
    :class:`Process`) and by extension a model interface.
    Some attributes such as `provided` and `optional` also contribute to
    the definition of the interface.

    """
    default_validators = []  # Default set of validators

    def __init__(self, allowed_dims, provided=False, optional=False,
                 default_value=None, validators=(), group=None,
                 description='', attrs=None):
        """
        Parameters
        ----------
        allowed_dims : str or tuple or list
            Dimension label(s) allowed for the variable. An empty tuple
            corresponds to a scalar variable, a string or a 1-length tuple
            corresponds to a 1-d variable and a n-length tuple corresponds to a
            n-d variable. A list of str or tuple items may also be provided if
            the variable accepts different numbers of dimensions.
            This should not include a time dimension, which is always allowed.
        provided : bool, optional
            Defines whether a value for the variable is required (False)
            or provided (True) by the process in which it is defined
            (default: False).
            If `provided=True`, then the variable in a process/model won't
            be considered as an input of that process/model.
        optional : bool, optional
            If True, a value may not be required for the variable
            (default: False). Ignored when `provided` is True.
        default_value : object, optional
            Single default value for the variable (default: None). It
            will be automatically broadcasted to all of its dimensions.
            Ignored when `provided` is True.
        validators : tuple or list, optional
            A list of callables that take an `xarray.Variable` object and
            raises a `ValidationError` if it doesn’t meet some criteria.
            It may be useful for custom, advanced validation that
            can be reused for different variables.
        group : str, optional
            Variable group.
        description : str, optional
            Short description of the variable (ideally one-line).
        attrs : dict, optional
            Dictionnary of additional metadata (e.g., standard_name,
            units, math_symbol...).

        """
        super(Variable, self).__init__(provided=provided, group=group,
                                       description=description,
                                       attrs=attrs)

        if not len(allowed_dims):
            allowed_dims = [()]
        if isinstance(allowed_dims, str):
            allowed_dims = [(allowed_dims,)]
        elif isinstance(allowed_dims, list):
            allowed_dims = [tuple([d]) if isinstance(d, str) else tuple(d)
                            for d in allowed_dims]
        elif len(allowed_dims) == 1:
            allowed_dims = [allowed_dims]
        self.allowed_dims = tuple(allowed_dims)

        self.optional = optional
        self.default_value = default_value
        self._validators = list(validators)
        self._state = None
        self._rate = None
        self._change = None

    @property
    def validators(self):
        return list(itertools.chain(self.default_validators, self._validators))

    def run_validators(self, xr_variable):
        for vfunc in self.validators:
            vfunc(xr_variable)

    def validate(self, xr_variable):
        pass

    def validate_dimensions(self, dims, ignore_dims=None):
        """Validate given dimensions for this variable.

        Parameters
        ----------
        dims : tuple
            Dimensions given for the variable.
        ignore_dims : tuple, optional
            Dimensions that are ignored during the validation process.
            Typically, it may correspond to a time dimension (time steps)
            and/or a grid-search or parameter-space dimension for this
            variable itself.

        Raises
        ------
        ValidationError
            If the given dimensions are not valid, i.e., if they doesn't
            correspond to allowed dimensions for this variable.

        """
        dims_dict = OrderedDict([(d, None) for d in dims])

        if ignore_dims is not None:
            for d in ignore_dims:
                dims_dict.pop(d, None)

        clean_dims = tuple(dims_dict)
        test_dims = [d for d in self.allowed_dims if d == clean_dims]

        if not test_dims:
            raise ValidationError("invalid dimensions %s\n"
                                  "allowed dimensions are %s"
                                  % (dims, self.allowed_dims))

    def to_xarray_variable(self, value):
        """Convert the input value to an `xarray.Variable` object.

        Parameters
        ----------
        value : object
            The input value can be in the form of a single value,
            an array-like, a ``(dims, data[, attrs])`` tuple, another
            `xarray.Variable` object or a `xarray.DataArray` object.
            if None, the `default_value` attribute is used to set the value.

        Returns
        -------
        variable : `xarray.Variable`
            A xarray Variable object whith data corresponding to the input (or
            default) value and with attributes ('description' and other
            key:value pairs found in `Variable.attrs`).

        """
        if value is None:
            value = self.default_value

        # in case where value is a 1-d array without dimension name,
        # dimension name is set to 'this_variable' and has to be renamed
        # later by the name of the variable in a process/model.
        xr_variable = as_variable(value, name='this_variable')

        xr_variable.attrs.update(self.attrs)
        if self.description:
            xr_variable.attrs['description'] = self.description

        return xr_variable

    @property
    def state(self):
        """State value of the variable, i.e., the instant value at a given
        time or simply the value if the variable is not time dependent.
        """
{'fraction_non_alphanumeric': 0.07267185912631223, 'fraction_numerical': 0.0006772773450728074, 'mean_word_length': 2.069216379131158, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4005684', 'n_tokens_mistral': 3628, 'n_tokens_neox': 3584, 'n_words': 1465}