# Copyright 2020 DeepMind Technologies Limited. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Utilities to emit warnings."""

import functools
import warnings


def warn_only_n_pos_args_in_future(fun, n):
  """Warns if more than ``n`` positional arguments are passed to ``fun``.

  For instance:
  >>> @functools.partial(chex.warn_only_n_pos_args_in_future, n=1)
  ... def f(a, b, c=1):
  ...   return a + b + c

  Will raise a DeprecationWarning if ``f`` is called with more than one
  positional argument (e.g. both f(1, 2, 3) and f(1, 2, c=3) raise a warning).

  Args:
    fun: the function to wrap.
    n: the number of positional arguments to allow.

  Returns:
    A wrapped function that emits a warning if more than `n` positional
    arguments are passed.
  """

  @functools.wraps(fun)
  def wrapper(*args, **kwargs):
    if len(args) > n:
      warnings.warn(
          f'only the first {n} arguments can be passed positionally '
          'additional args will become keyword-only soon',
          DeprecationWarning,
          stacklevel=2
          )
    return fun(*args, **kwargs)

  return wrapper


warn_keyword_args_only_in_future = functools.partial(
    warn_only_n_pos_args_in_future, n=0
)


def warn_deprecated_function(fun, replacement):
  """A decorator to mark a function definition as deprecated.

  Example usage:
  @warn_deprecated_function(fun, replacement='g')
  def f(a, b):
    return a + b

  Args:
    fun: the deprecated function.
    replacement: the name of the function to be used instead.

  Returns:
    the wrapped function.
  """

  @functools.wraps(fun)
  def new_fun(*args, **kwargs):
    warnings.warn(
        f'The function {fun.__name__} is deprecated, '
        f'please use {replacement} instead.',
        category=DeprecationWarning,
        stacklevel=2)
    return fun(*args, **kwargs)
  return new_fun
