docs: def solve(self, value, args=None, kwargs=None):
          """Try to get the current attribute/function result for the given value.
  
          If ``args`` or ``kwargs`` are passed, the attribute got from ``value`` must be callable.
          They will be passed anyway (using ``[]`` and ``{}`` as default values) if the attribute
          is a function.
  
          And if it's not a function but a real attribute, if no args are passed but the attribute
          got from ``value`` is "callable", we'll try to call it without argument.
          If any exception occurs during the call, two things may happen:
          - the "thing" called is a function or method, then we raise the exception
          - it's something else, like an instance of a class having a ``__call__`` method,
            in this case we want to return the instance (if the user want the thing to be
            called it could still use parentheses)
  
          Arguments
          ---------
          value : ?
              The value from which we want the attribute.
          args : list, default ``None``
              If defined, list of non-named arguments that will be passed to the attribute if it's
              callable.
          kwargs : dict, default ``None``
              If defined, list of named arguments that will be passed to the attribute if it's
              callable.
  
          Returns
          -------
          Value of the current attribute/function result for the ``value`` instance.
  
          Raises
          ------
          dataql.solvers.exceptions.CallableError
              If an exception was raised when the attribute/function was called.
  
          Example
          -------
  
          >>> from datetime import date
          >>> d = date(2015, 6, 1)
          >>> Attribute('day').solve(d)
          1
          >>> Attribute('strftime').solve(d, ['%F'])
          '2015-06-01'
          >>> Attribute('day').solve('a string') # doctest: +ELLIPSIS
          Traceback (most recent call last):
          dataql...AttributeNotFound: `<Attribute 'day'>` is not an allowed attribute
          >>> Attribute('day').solve(d, ['foo'])
          Traceback (most recent call last):
          TypeError: 'int' object is not callable
          >>> Attribute('str', function=str).solve(d)
          '2015-06-01'
  
          # Test the dict-like approach
          >>> class MyDict(dict):
          ...     foo = 1
          >>> d = MyDict(bar=2)
          >>> Attribute('foo').solve(d)
          1
          >>> Attribute('bar').solve(d)
          2
  
          Example with callable
          ----------------------
  
          >>> class Klass1:
          ...     def __call__(self):
          ...         return 'foo'
          >>> class Klass2:
          ...     def __call__(self, arg):
          ...         return 'foo'
          >>> class Klass3:
          ...     def __call__(self):
          ...         raise TypeError('error')
          >>> class Klass4:
          ...     def __call__(self, **kwargs):
          ...         return 'foo'
          >>> class Klass5:
          ...     def __call__(self, **kwargs):
          ...         return kwargs['foo']
          >>> class Klass6:
          ...     def __call__(self, *args):
          ...         return 'foo'
          >>> class Klass7:
          ...     def __call__(self, *args):
          ...         return args[0]
          >>> class Klass:
          ...     def call1(self):
          ...         return 'foo'
          ...     def call2(self, arg):
          ...         return 'foo'
          ...     def call3(self):
          ...         raise TypeError('error')
          ...     def call4(self, **kwargs):
          ...         return 'foo'
          ...     def call5(self, **kwargs):
          ...         return kwargs['foo']
          ...     def call6(self, *args):
          ...         return 'foo'
          ...     def call7(self, *args):
          ...         return args[0]
          ...     k1, k2, k3, k4 = Klass1(), Klass2(), Klass3(), Klass4()
          ...     k5, k6, k7 = Klass5(), Klass6(), Klass7()
  
          >>> v = Klass()
  
          # Can be called without arguments: we call it
          >>> Attribute('call1').solve(v)
          'foo'
  
          # TypeError while calling without argument, but it's a method: raise
          >>> Attribute('call2').solve(v) # doctest: +ELLIPSIS
          Traceback (most recent call last):
          dataql.solvers.exceptions.CallableError:...calling `call2` (without arguments)
  
          # TypeError but no argument needed, but it's a method: raise
          >>> Attribute('call3').solve(v) # doctest: +ELLIPSIS
          Traceback (most recent call last):
          dataql.solvers.exceptions.CallableError:...calling `call3` (without arguments)
  
          # Can be called without used kwargs: we call it
          >>> Attribute('call4').solve(v)
          'foo'
  
          # KeyError while calling without argument, in a get of kwargs, but it's a method: raise
          >>> Attribute('call5').solve(v) # doctest: +ELLIPSIS
          Traceback (most recent call last):
          dataql.solvers.exceptions.CallableError:...calling `call5` (without arguments)
  
          # Can be called without used args: we call it
          >>> Attribute('call6').solve(v)
          'foo'
  
          # IndexError while calling without argument, in a get of args, but it's a method: raise
          >>> Attribute('call7').solve(v) # doctest: +ELLIPSIS
          Traceback (most recent call last):
          dataql.solvers.exceptions.CallableError:...calling `call7` (without arguments)
  
  
  
          # Can be called without arguments: we call it
          >>> Attribute('k1').solve(v)
          'foo'
  
          # TypeError while calling without argument, but it's NOT a method: return not called
          >>> isinstance(Attribute('k2').solve(v), Klass2)
          True
  
          # TypeError but no argument needed, but it's NOT a method: return not called
          >>> isinstance(Attribute('k3').solve(v), Klass3)
          True
  
          # Can be called without used kwargs: we call it
          >>> Attribute('k4').solve(v)
          'foo'
  
          # KeyError while calling without argument, in a get of kwargs, but it's NOT a method:
          # return not called
          >>> isinstance(Attribute('k5').solve(v), Klass5)
          True
  
          # Can be called without used args: we call it
          >>> Attribute('k6').solve(v)
          'foo'
  
          # IndexError while calling without argument, in a get of args, but it's NOT a method:
          # return not called
          >>> isinstance(Attribute('k7').solve(v), Klass7)
          True
  
          """
  
          # If we have a function, apply this, using the value as first argument,
          # then args and kwargs.
          if self.function:
              try:
                  return self.function(value, *(args or []), **(kwargs or {}))
              except Exception as ex:
                  raise CallableError(self, value, args, kwargs, ex)
  
          # Manage a normal attribute.
          try:
              # try to get an attribute.
              result = getattr(value, self.name)
          except AttributeError:
              try:
                  # Attribute not found, try to get a key.
                  result = value[self.name]
              except (TypeError, KeyError):
                  # No attribute, no key.
                  raise AttributeNotFound(self)
  
          # We make a call from the attribute in all cases if we have some arguments.
          if args is not None or kwargs is not None:
              result = result(*(args or []), **(kwargs or {}))
  
          elif not isclass(result) and callable(result):
              # We have no arguments, but the attribute is a callable, so, if it not a class,
              # we'll try to call it, without arguments.
              try:
                  result = result()
  
              except Exception as ex:
                  # If an exception is raised during the call, we have two cases:
                  # - the "thing" called is a function or method, then we re-raise the exception
                  # - it's something else, like an instance of a class having a ``__call__`` method,
                  #   in this case we want to return the instance (if the user want the thing to be
                  #   called it could still use parentheses)
                  if isfunction(result) or ismethod(result):
                      raise CallableError(self, value, args, kwargs, ex)
  
          return result