<commit_before># -*- coding: utf-8 -*-

"""Miscellaneous convenience functions.
"""

import ast
import functools
import operator
import os
import shutil
import subprocess
import collections
import itertools
import traceback

from warnings import warn
from functools import (partial, wraps)

import xarray
import pandas
import numpy as np


__all__ = [
    'deprecated',
    'extract_block_diag',
    'safe_eval',
    'unique',
    'path_append',
    'path_prepend',
    'path_remove',
    'get_time_dimensions',
    'get_time_coordinates',
    'concat_each_time_coordinate',
    'undo_xarray_floatification',
    'image2mpeg',
    'stack_xarray_repdim',
    'split_units',
    'reraise_with_stack',
    'get_xarray_groups',
]


def deprecated(func=None, new_name=None, message=None):
    """Decorator which can be used to mark functions as deprecated.

    Examples:
        Calling ``foo()`` will raise a ``DeprecationWarning``.

        >>> @deprecated
        ... def deprecated_function():
        ...     pass

        Display message with additional information:

        >>> @deprecated(message='Additional information message.')
        ... def deprecated_function():
        ...     pass
    """
    # Return partial when no arguments are passed.
    # This allows a plain call of the decorator.
    if func is None:
        return partial(deprecated, new_name=new_name, message=message)

    # Build warning message (with optional information).
    msg = f'\nCall to deprecated function `{func.__name__}`.'

    if new_name is not None:
        msg += f' Use `{new_name}` instead.'

    if message is not None:
        msg += f'\n{message}'

    # Wrapper that prints the warning before calling the deprecated function.
    @wraps(func)
    def wrapper(*args, **kwargs):
        warn(msg, category=DeprecationWarning, stacklevel=2)
        return func(*args, **kwargs)

    if wrapper.__doc__ is None:
        wrapper.__doc__ = ''

    wrapper.__doc__ += (
        '\n\n    .. warning::\n       Function is deprecated'
        ' and will be removed in a future version.'
    )

    if new_name is not None:
        wrapper.__doc__ += f' Use :func:`{new_name}` instead.'

    if message is not None:
        wrapper.__doc__ += f'\n\n       {message}'

    return wrapper


def extract_block_diag(M, n):
    """Extract diagonal blocks from square Matrix.

    Args:
        M (np.array): Square matrix.
        n (int): Number of blocks to extract.

    Example:
        >>> foo = np.array([[ 1.,  1.,  0.,  0.],
        ... [ 1.,  1.,  0.,  0.],<commit_msg>Add comments to `deprecated` decorator.<commit_after># -*- coding: utf-8 -*-

"""Miscellaneous convenience functions.
"""

import ast
import functools
import operator
import os
import shutil
import subprocess
import collections
import itertools
import traceback

from warnings import warn
from functools import (partial, wraps)

import xarray
import pandas
import numpy as np


__all__ = [
    'deprecated',
    'extract_block_diag',
    'safe_eval',
    'unique',
    'path_append',
    'path_prepend',
    'path_remove',
    'get_time_dimensions',
    'get_time_coordinates',
    'concat_each_time_coordinate',
    'undo_xarray_floatification',
    'image2mpeg',
    'stack_xarray_repdim',
    'split_units',
    'reraise_with_stack',
    'get_xarray_groups',
]


def deprecated(func=None, new_name=None, message=None):
    """Decorator which can be used to mark functions as deprecated.

    Examples:
        Calling ``foo()`` will raise a ``DeprecationWarning``.

        >>> @deprecated
        ... def deprecated_function():
        ...     pass

        Display message with additional information:

        >>> @deprecated(message='Additional information message.')
        ... def deprecated_function():
        ...     pass
    """
    # Return partial when no arguments are passed.
    # This allows a plain call of the decorator.
    if func is None:
        return partial(deprecated, new_name=new_name, message=message)

    # Build warning message (with optional information).
    msg = f'\nCall to deprecated function `{func.__name__}`.'

    if new_name is not None:
        msg += f' Use `{new_name}` instead.'

    if message is not None:
        msg += f'\n{message}'

    # Wrapper that prints the warning before calling the deprecated function.
    @wraps(func)
    def wrapper(*args, **kwargs):
        warn(msg, category=DeprecationWarning, stacklevel=2)
        return func(*args, **kwargs)

    if wrapper.__doc__ is None:
        wrapper.__doc__ = ''

    # Lines added to the docstring need to be indented with four spaces as this
    # is technically the case for all lines except the first one.
    # If this is not done, the Sphinx build will produce wrong results as the
    # relative indentation of the added lines and the original docstring does
    # not match.
    wrapper.__doc__ += (
        '\n\n    .. warning::\n       Function is deprecated'
        ' and will be removed in a future version.'
    )

    if new_name is not None:
        wrapper.__doc__ += f' Use :func:`{new_name}` instead.'

    if message is not None:
        wrapper.__doc__ += f'\n\n       {message}'

    return wrapper


def extract_block_diag(M, n):
    """Extract diagonal blocks from square Matrix.

    Args:
        M (np.array): Square matrix.
        n (int): Number of blocks to extract.

    Example:
        >>> foo = np.array([[ 1.,  1.,  0.,  0.],
        ... [ 1.,  1.,  0.,  0.],
{'fraction_non_alphanumeric': 0.1097316048931897, 'fraction_numerical': 0.004016797516888808, 'mean_word_length': 2.6350364963503647, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4589439', 'n_tokens_mistral': 1629, 'n_tokens_neox': 1584, 'n_words': 529}