#!/usr/bin/env python

# ###########################################################################
#
# This file is part of Taurus
#
# http://taurus-scada.org
#
# Copyright 2011 CELLS / ALBA Synchrotron, Bellaterra, Spain
#
# Taurus is free software: you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Taurus 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 Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Taurus.  If not, see <http://www.gnu.org/licenses/>.
#
# ###########################################################################

"""
.. currentmodule:: taurus.core.evaluation

Evaluation extension for taurus core model.

The evaluation extension is a special extension that provides evaluation
objects. The official scheme name is 'eval'.

The main usage for this extension is to provide a way of performing
mathematical evaluations with data from other source, as well as allowing fast
interfacing with sources of data not supported by specific schemes.

The Evaluation Factory (:class:`EvaluationFactory`) uses the following object
naming for referring to attributes (:class:`EvaluationAttribute`):

    `eval:[//<authority>][@<evaluator>/][<subst>;]<expr>`

or the following for referring to evaluation devices
(:class:`EvaluationDevice`):

    `eval:[//<authority>]@<evaluator>`

or the following for referring to an evaluation authority
(:class:`EvaluationAuthority`):

    `eval://<authority>`

where:
    - The `<authority>` segment is optional (except when referring to an
      EvaluationAuthority). At this point, only `//localhost` is supported.
    - The `@<evaluator>` is optional (except when referring to an
      EvaluationDevice). If not given, it defaults to `DefaultEvaluator`. See
      below for further details
    - `<expr>` is a mathematical expression (using python syntax) that may have
      references to other taurus **attributes** by enclosing them between `{`
      and `}`. Expressions will be evaluated by the evaluator device to which
      the attribute is assigned.
    - The optional `<subst>` segment is used to provide substitution symbols.
      `<subst>` is a semicolon-separated string of `<key>=<value>` strings.

The evaluator device inherits from :class:`SafeEvaluator` which by default
includes a large subset of mathematical functions from the :mod:`numpy`
module. If access to other symbols are required, a custom evaluator can
be used. `<evaluator>` is a unique identification name for the evaluator
device object and may define a source of additional symbols to be present
for the evaluation. The supported syntax for `@<evaluator>` is:

    - `@<ID>` (cannot contain dots or any of `/` `?` `#` `:` `=`). This
      indicates just an alternative name for the EvaluationDevice, It does not
      add any extra symbol to the evaluation context.

    - `@<modulename>.*` (<modulename> may include dots for submodules). It
      will make all symbols found in the given module available during the
      evaluation (i.e., it emulates doing `from <modulename> import *` in the
      evaluation context).

    - `@<modulename>.<customdeviceclass>`. Use your own custom EvaluationDevice
      based class. This allows to define custom symbols see
      :file:`<taurus>/core/evaluation/test/res/dev_example.py`, **but** note
      that this syntax is is now superseded by the "instance-based" one
      (see below), which is easier to use and provides write attribute support.

    - `@<inst>=<modulename>.<class>()` (e.g. `@c=mymod.MyClass()` ). This
      will import a class from a module, then instantiate it and then
      make the instance available for evaluation with the given name. Note that
      the `<inst>=` part may be omitted, in which case the instance will be
      available for evaluation as `self`. **IMPORTANT:** If the given class
      declares writable properties, EvaluationAttributes that access one such
      property will automatically be considered writable. See examples of usage
      in :file:`<taurus>/core/evaluation/test/res/mymod.py` and in
      :file:`<taurus>/core/evaluation/test/res/ipap_example.py`


Some examples of valid evaluation models are:

    - An attribute that multiplies a tango attribute by 2::

        eval:2*{tango:a/b/c/d}

    - Same as above, but using substitutions::

        eval:k=2;a={tango:a/b/c/d};k*a

    - An attribute that adds two tango attributes together (assuming that tango
      is set as the default scheme)::

        eval:{a/b/c/d}+{f/g/h/i}

    - An attribute that generates an array of random values::

        eval:rand(256)

    - Same as above, but with units::

        eval:Q(rand(256),'V')

    - An attribute that adds noise to a tango image attribute::

        eval:img={tango:sys/tg_test/1/short_image_ro};img+10*rand(*img.shape)

    - An attribute that accesses a method from a given module (in this
      case to use os.path.exists)::

        eval:@os.*/path.exists("/some/file")

    - Same as before, for getting today's date as an attribute::

        eval:@datetime.*/date.today().isoformat()

    - A default evaluator device named `foo`::

        eval:@foo

    - A custom evaluator device (implemented as class `MyEvalDev` in the
      `mymod` module)::

        eval:@mymod.MyEvalDev

    - A custom evaluator device (implemented as class `MyEvalDev` in the
      `mymod` module)::

        eval:@mymod.MyEvalDev

    - A writable attribute foo (implemented as a writable property of the
      `MyClass` class from the `mymod` module)::

        eval:@c=mymod.MyClass()/c.foo

      assuming that the `mymod` module defines `MyClass` as::

        class MyClass(object):
            (...)
            get_foo(self):
                (...)
            set_foo(self, value):
                (...)
            foo = property(get_foo, set_foo)
            (...)


.. note:: Previous to SEP3, a RFC3986 non-compliant syntax was used for the
          evaluation scheme (e.g., allowing names such as
          ``eval://db=foo;dev=bar;a*b?k=2;a={tango:a/b/c/d}``).
          This syntax is now deprecated and should not be used. Taurus will
          issue warnings if detected.
"""

from .evalfactory import EvaluationFactory  # noqa: F401
from .evalattribute import EvaluationAttribute  # noqa: F401
from .evalauthority import EvaluationAuthority  # noqa: F401
from .evaldevice import EvaluationDevice  # noqa: F401


__all__ = [
    "EvaluationFactory",
    "EvaluationAttribute",
    "EvaluationAuthority",
    "EvaluationDevice",
]
