Source code for skutil.base

# -*- coding: utf-8 -*-

from __future__ import absolute_import, division, print_function
from sklearn.base import BaseEstimator, TransformerMixin
from sklearn.externals import six
from abc import ABCMeta
import re
import warnings

__all__ = [
    'overrides',
    'since',
    'suppress_warnings',
    'BaseSkutil',
    'SelectiveMixin'
]


def overrides(interface_class):
    """Decorator for methods that override super methods. Provides
    runtime validation that the method is, in fact, inherited from the
    superclass. If not, will raise an ``AssertionError``.

    Parameters
    ----------

    interface_class : type
        The class/type from which the specified method is inherited.
        If the method does not exist in the specified type, a ``RuntimeError``
        will be raised.


    Examples
    --------
    
    The following is valid use:

        >>> class A(object):
        ...     def a(self):
        ...         return 1

        >>> class B(A):
        ...     @overrides(A)
        ...     def a(self):
        ...         return 2
        ...
        ...     def b(self):
        ...         return 0

    The following would be an invalid ``overrides`` statement, since
    ``A`` does not have a ``b`` method to override.

        >>> class C(B): # doctest: +IGNORE_EXCEPTION_DETAIL
        ...     @overrides(A) # should override B, not A
        ...     def b(self):
        ...         return 1
        Traceback (most recent call last):  
        AssertionError: A.b must override a super method!


    .. versionadded:: 0.1.0
    """

    def overrider(method):
        assert (method.__name__ in dir(interface_class)), '%s.%s must override a super method!' % (
            interface_class.__name__, method.__name__)
        return method

    return overrider


def since(version):
    """A decorator that annotates a function to append the version 
    of skutil the function was added. This decorator is an adaptation of PySpark's.

    Parameters
    ----------

    version : str, float or int
        The version the specified method was added to skutil.


    Examples
    --------

        >>> @since('0.1.5')
        ... def some_fun():
        ...     '''Some docstring'''
        ...     return None
        ...
        >>>
        >>> some_fun.__doc__ # doctest: +SKIP
        'Some docstring\n\n.. versionadded:: 0.1.5'


    .. versionadded:: 0.1.5
    """
    indent_p = re.compile(r'\n( +)')

    def deco(f):
        indents = indent_p.findall(f.__doc__)
        indent = ' ' * (min(len(m) for m in indents) if indents else 0)
        f.__doc__ = f.__doc__.rstrip() + "\n\n%s.. versionadded:: %s" % (indent, version)
        return f

    return deco


def suppress_warnings(func):
    """Decorator that forces a method to suppress
    all warnings it may raise. This should be used with caution,
    as it may complicate debugging. For internal purposes, this is
    used for imports that cause consistent warnings (like pandas or
    matplotlib)

    Parameters
    ----------

    func : callable
        Automatically passed to the decorator. This
        function is run within the context of the warning
        filterer.


    Examples
    --------

    When any function is decorated with the ``suppress_warnings``
    decorator, any warnings that are raised will be suppressed.

        >>> import warnings
        >>>
        >>> @suppress_warnings
        ... def fun_that_warns():
        ...     warnings.warn("This is a warning", UserWarning)
        ...     return 1
        >>>
        >>> fun_that_warns()
        1


    .. versionadded:: 0.1.0
    """

    def suppressor(*args, **kwargs):
        with warnings.catch_warnings(record=True):
            warnings.simplefilter("ignore")
            return func(*args, **kwargs)

    return suppressor


[docs]class SelectiveMixin: """A mixin class that all selective transformers should implement. All ``SelectiveMixin`` implementers should only apply their ``fit`` method on the defined columns. """
# at one time, this contained methods. But They've since # been weeded out one-by-one... do we want to keep it? # TODO: in future versions, remove this mixin or add # concrete functionality class BaseSkutil(six.with_metaclass(ABCMeta, BaseEstimator, SelectiveMixin)): """Provides the base class for all non-h2o skutil transformers. Implements ``SelectiveMixin``. Also provides the "pretty-print" ``__repr__`` implemented in sklearn's ``BaseEstimator``. Parameters ---------- cols : array_like, shape=(n_features,), optional (default=None) The names of the columns on which to apply the transformation. If no column names are provided, the transformer will be ``fit`` on the entire frame. Note that the transformation will also only apply to the specified columns, and any other non-specified columns will still be present after transformation. as_df : bool, optional (default=True) Whether to return a Pandas ``DataFrame`` in the ``transform`` method. If False, will return a Numpy ``ndarray`` instead. Since most skutil transformers depend on explicitly-named ``DataFrame`` features, the ``as_df`` parameter is True by default. Examples -------- >>> from skutil.base import BaseSkutil >>> class A(BaseSkutil): ... def __init__(self, cols=None, as_df=None): ... super(A, self).__init__(cols, as_df) ... >>> A() A(as_df=None, cols=None) """ def __init__(self, cols=None, as_df=True): self.cols = cols self.as_df = as_df