Static code analysis and corrections
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
from pandas.util._decorators import Appender, Substitution, cache_readonly # noqa
|
||||
from pandas.core.util.hashing import hash_pandas_object, hash_array # noqa
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,352 @@
|
||||
from functools import wraps
|
||||
import inspect
|
||||
from textwrap import dedent
|
||||
import warnings
|
||||
|
||||
from pandas._libs.properties import cache_readonly # noqa
|
||||
from pandas.compat import PY2, callable, signature
|
||||
|
||||
|
||||
def deprecate(name, alternative, version, alt_name=None,
|
||||
klass=None, stacklevel=2, msg=None):
|
||||
"""Return a new function that emits a deprecation warning on use.
|
||||
|
||||
To use this method for a deprecated function, another function
|
||||
`alternative` with the same signature must exist. The deprecated
|
||||
function will emit a deprecation warning, and in the docstring
|
||||
it will contain the deprecation directive with the provided version
|
||||
so it can be detected for future removal.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
name : str
|
||||
Name of function to deprecate.
|
||||
alternative : func
|
||||
Function to use instead.
|
||||
version : str
|
||||
Version of pandas in which the method has been deprecated.
|
||||
alt_name : str, optional
|
||||
Name to use in preference of alternative.__name__.
|
||||
klass : Warning, default FutureWarning
|
||||
stacklevel : int, default 2
|
||||
msg : str
|
||||
The message to display in the warning.
|
||||
Default is '{name} is deprecated. Use {alt_name} instead.'
|
||||
"""
|
||||
|
||||
alt_name = alt_name or alternative.__name__
|
||||
klass = klass or FutureWarning
|
||||
warning_msg = msg or '{} is deprecated, use {} instead'.format(name,
|
||||
alt_name)
|
||||
|
||||
@wraps(alternative)
|
||||
def wrapper(*args, **kwargs):
|
||||
warnings.warn(warning_msg, klass, stacklevel=stacklevel)
|
||||
return alternative(*args, **kwargs)
|
||||
|
||||
# adding deprecated directive to the docstring
|
||||
msg = msg or 'Use `{alt_name}` instead.'.format(alt_name=alt_name)
|
||||
doc_error_msg = ('deprecate needs a correctly formatted docstring in '
|
||||
'the target function (should have a one liner short '
|
||||
'summary, and opening quotes should be in their own '
|
||||
'line). Found:\n{}'.format(alternative.__doc__))
|
||||
|
||||
# when python is running in optimized mode (i.e. `-OO`), docstrings are
|
||||
# removed, so we check that a docstring with correct formatting is used
|
||||
# but we allow empty docstrings
|
||||
if alternative.__doc__:
|
||||
if alternative.__doc__.count('\n') < 3:
|
||||
raise AssertionError(doc_error_msg)
|
||||
empty1, summary, empty2, doc = alternative.__doc__.split('\n', 3)
|
||||
if empty1 or empty2 and not summary:
|
||||
raise AssertionError(doc_error_msg)
|
||||
wrapper.__doc__ = dedent("""
|
||||
{summary}
|
||||
|
||||
.. deprecated:: {depr_version}
|
||||
{depr_msg}
|
||||
|
||||
{rest_of_docstring}""").format(summary=summary.strip(),
|
||||
depr_version=version,
|
||||
depr_msg=msg,
|
||||
rest_of_docstring=dedent(doc))
|
||||
|
||||
return wrapper
|
||||
|
||||
|
||||
def deprecate_kwarg(old_arg_name, new_arg_name, mapping=None, stacklevel=2):
|
||||
"""
|
||||
Decorator to deprecate a keyword argument of a function.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
old_arg_name : str
|
||||
Name of argument in function to deprecate
|
||||
new_arg_name : str or None
|
||||
Name of preferred argument in function. Use None to raise warning that
|
||||
``old_arg_name`` keyword is deprecated.
|
||||
mapping : dict or callable
|
||||
If mapping is present, use it to translate old arguments to
|
||||
new arguments. A callable must do its own value checking;
|
||||
values not found in a dict will be forwarded unchanged.
|
||||
|
||||
Examples
|
||||
--------
|
||||
The following deprecates 'cols', using 'columns' instead
|
||||
|
||||
>>> @deprecate_kwarg(old_arg_name='cols', new_arg_name='columns')
|
||||
... def f(columns=''):
|
||||
... print(columns)
|
||||
...
|
||||
>>> f(columns='should work ok')
|
||||
should work ok
|
||||
|
||||
>>> f(cols='should raise warning')
|
||||
FutureWarning: cols is deprecated, use columns instead
|
||||
warnings.warn(msg, FutureWarning)
|
||||
should raise warning
|
||||
|
||||
>>> f(cols='should error', columns="can\'t pass do both")
|
||||
TypeError: Can only specify 'cols' or 'columns', not both
|
||||
|
||||
>>> @deprecate_kwarg('old', 'new', {'yes': True, 'no': False})
|
||||
... def f(new=False):
|
||||
... print('yes!' if new else 'no!')
|
||||
...
|
||||
>>> f(old='yes')
|
||||
FutureWarning: old='yes' is deprecated, use new=True instead
|
||||
warnings.warn(msg, FutureWarning)
|
||||
yes!
|
||||
|
||||
To raise a warning that a keyword will be removed entirely in the future
|
||||
|
||||
>>> @deprecate_kwarg(old_arg_name='cols', new_arg_name=None)
|
||||
... def f(cols='', another_param=''):
|
||||
... print(cols)
|
||||
...
|
||||
>>> f(cols='should raise warning')
|
||||
FutureWarning: the 'cols' keyword is deprecated and will be removed in a
|
||||
future version please takes steps to stop use of 'cols'
|
||||
should raise warning
|
||||
>>> f(another_param='should not raise warning')
|
||||
should not raise warning
|
||||
|
||||
>>> f(cols='should raise warning', another_param='')
|
||||
FutureWarning: the 'cols' keyword is deprecated and will be removed in a
|
||||
future version please takes steps to stop use of 'cols'
|
||||
should raise warning
|
||||
"""
|
||||
|
||||
if mapping is not None and not hasattr(mapping, 'get') and \
|
||||
not callable(mapping):
|
||||
raise TypeError("mapping from old to new argument values "
|
||||
"must be dict or callable!")
|
||||
|
||||
def _deprecate_kwarg(func):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
old_arg_value = kwargs.pop(old_arg_name, None)
|
||||
|
||||
if new_arg_name is None and old_arg_value is not None:
|
||||
msg = (
|
||||
"the '{old_name}' keyword is deprecated and will be "
|
||||
"removed in a future version. "
|
||||
"Please take steps to stop the use of '{old_name}'"
|
||||
).format(old_name=old_arg_name)
|
||||
warnings.warn(msg, FutureWarning, stacklevel=stacklevel)
|
||||
kwargs[old_arg_name] = old_arg_value
|
||||
return func(*args, **kwargs)
|
||||
|
||||
if old_arg_value is not None:
|
||||
if mapping is not None:
|
||||
if hasattr(mapping, 'get'):
|
||||
new_arg_value = mapping.get(old_arg_value,
|
||||
old_arg_value)
|
||||
else:
|
||||
new_arg_value = mapping(old_arg_value)
|
||||
msg = ("the {old_name}={old_val!r} keyword is deprecated, "
|
||||
"use {new_name}={new_val!r} instead"
|
||||
).format(old_name=old_arg_name,
|
||||
old_val=old_arg_value,
|
||||
new_name=new_arg_name,
|
||||
new_val=new_arg_value)
|
||||
else:
|
||||
new_arg_value = old_arg_value
|
||||
msg = ("the '{old_name}' keyword is deprecated, "
|
||||
"use '{new_name}' instead"
|
||||
).format(old_name=old_arg_name,
|
||||
new_name=new_arg_name)
|
||||
|
||||
warnings.warn(msg, FutureWarning, stacklevel=stacklevel)
|
||||
if kwargs.get(new_arg_name, None) is not None:
|
||||
msg = ("Can only specify '{old_name}' or '{new_name}', "
|
||||
"not both").format(old_name=old_arg_name,
|
||||
new_name=new_arg_name)
|
||||
raise TypeError(msg)
|
||||
else:
|
||||
kwargs[new_arg_name] = new_arg_value
|
||||
return func(*args, **kwargs)
|
||||
return wrapper
|
||||
return _deprecate_kwarg
|
||||
|
||||
|
||||
def rewrite_axis_style_signature(name, extra_params):
|
||||
def decorate(func):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
return func(*args, **kwargs)
|
||||
|
||||
if not PY2:
|
||||
kind = inspect.Parameter.POSITIONAL_OR_KEYWORD
|
||||
params = [
|
||||
inspect.Parameter('self', kind),
|
||||
inspect.Parameter(name, kind, default=None),
|
||||
inspect.Parameter('index', kind, default=None),
|
||||
inspect.Parameter('columns', kind, default=None),
|
||||
inspect.Parameter('axis', kind, default=None),
|
||||
]
|
||||
|
||||
for pname, default in extra_params:
|
||||
params.append(inspect.Parameter(pname, kind, default=default))
|
||||
|
||||
sig = inspect.Signature(params)
|
||||
|
||||
func.__signature__ = sig
|
||||
return wrapper
|
||||
return decorate
|
||||
|
||||
# Substitution and Appender are derived from matplotlib.docstring (1.1.0)
|
||||
# module http://matplotlib.org/users/license.html
|
||||
|
||||
|
||||
class Substitution(object):
|
||||
"""
|
||||
A decorator to take a function's docstring and perform string
|
||||
substitution on it.
|
||||
|
||||
This decorator should be robust even if func.__doc__ is None
|
||||
(for example, if -OO was passed to the interpreter)
|
||||
|
||||
Usage: construct a docstring.Substitution with a sequence or
|
||||
dictionary suitable for performing substitution; then
|
||||
decorate a suitable function with the constructed object. e.g.
|
||||
|
||||
sub_author_name = Substitution(author='Jason')
|
||||
|
||||
@sub_author_name
|
||||
def some_function(x):
|
||||
"%(author)s wrote this function"
|
||||
|
||||
# note that some_function.__doc__ is now "Jason wrote this function"
|
||||
|
||||
One can also use positional arguments.
|
||||
|
||||
sub_first_last_names = Substitution('Edgar Allen', 'Poe')
|
||||
|
||||
@sub_first_last_names
|
||||
def some_function(x):
|
||||
"%s %s wrote the Raven"
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
if (args and kwargs):
|
||||
raise AssertionError("Only positional or keyword args are allowed")
|
||||
|
||||
self.params = args or kwargs
|
||||
|
||||
def __call__(self, func):
|
||||
func.__doc__ = func.__doc__ and func.__doc__ % self.params
|
||||
return func
|
||||
|
||||
def update(self, *args, **kwargs):
|
||||
"""
|
||||
Update self.params with supplied args.
|
||||
|
||||
If called, we assume self.params is a dict.
|
||||
"""
|
||||
|
||||
self.params.update(*args, **kwargs)
|
||||
|
||||
@classmethod
|
||||
def from_params(cls, params):
|
||||
"""
|
||||
In the case where the params is a mutable sequence (list or dictionary)
|
||||
and it may change before this class is called, one may explicitly use a
|
||||
reference to the params rather than using *args or **kwargs which will
|
||||
copy the values and not reference them.
|
||||
"""
|
||||
result = cls()
|
||||
result.params = params
|
||||
return result
|
||||
|
||||
|
||||
class Appender(object):
|
||||
"""
|
||||
A function decorator that will append an addendum to the docstring
|
||||
of the target function.
|
||||
|
||||
This decorator should be robust even if func.__doc__ is None
|
||||
(for example, if -OO was passed to the interpreter).
|
||||
|
||||
Usage: construct a docstring.Appender with a string to be joined to
|
||||
the original docstring. An optional 'join' parameter may be supplied
|
||||
which will be used to join the docstring and addendum. e.g.
|
||||
|
||||
add_copyright = Appender("Copyright (c) 2009", join='\n')
|
||||
|
||||
@add_copyright
|
||||
def my_dog(has='fleas'):
|
||||
"This docstring will have a copyright below"
|
||||
pass
|
||||
"""
|
||||
|
||||
def __init__(self, addendum, join='', indents=0):
|
||||
if indents > 0:
|
||||
self.addendum = indent(addendum, indents=indents)
|
||||
else:
|
||||
self.addendum = addendum
|
||||
self.join = join
|
||||
|
||||
def __call__(self, func):
|
||||
func.__doc__ = func.__doc__ if func.__doc__ else ''
|
||||
self.addendum = self.addendum if self.addendum else ''
|
||||
docitems = [func.__doc__, self.addendum]
|
||||
func.__doc__ = dedent(self.join.join(docitems))
|
||||
return func
|
||||
|
||||
|
||||
def indent(text, indents=1):
|
||||
if not text or not isinstance(text, str):
|
||||
return ''
|
||||
jointext = ''.join(['\n'] + [' '] * indents)
|
||||
return jointext.join(text.split('\n'))
|
||||
|
||||
|
||||
def make_signature(func):
|
||||
"""
|
||||
Returns a tuple containing the paramenter list with defaults
|
||||
and parameter list.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> def f(a, b, c=2):
|
||||
>>> return a * b * c
|
||||
>>> print(make_signature(f))
|
||||
(['a', 'b', 'c=2'], ['a', 'b', 'c'])
|
||||
"""
|
||||
|
||||
spec = signature(func)
|
||||
if spec.defaults is None:
|
||||
n_wo_defaults = len(spec.args)
|
||||
defaults = ('',) * n_wo_defaults
|
||||
else:
|
||||
n_wo_defaults = len(spec.args) - len(spec.defaults)
|
||||
defaults = ('',) * n_wo_defaults + tuple(spec.defaults)
|
||||
args = []
|
||||
for var, default in zip(spec.args, defaults):
|
||||
args.append(var if default == '' else var + '=' + repr(default))
|
||||
if spec.varargs:
|
||||
args.append('*' + spec.varargs)
|
||||
if spec.keywords:
|
||||
args.append('**' + spec.keywords)
|
||||
return args, spec.args
|
||||
@@ -0,0 +1,103 @@
|
||||
"""
|
||||
This module houses a utility class for mocking deprecated modules.
|
||||
It is for internal use only and should not be used beyond this purpose.
|
||||
"""
|
||||
|
||||
import importlib
|
||||
import warnings
|
||||
|
||||
|
||||
class _DeprecatedModule(object):
|
||||
""" Class for mocking deprecated modules.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
deprmod : name of module to be deprecated.
|
||||
deprmodto : name of module as a replacement, optional.
|
||||
If not given, the __module__ attribute will
|
||||
be used when needed.
|
||||
removals : objects or methods in module that will no longer be
|
||||
accessible once module is removed.
|
||||
moved : dict, optional
|
||||
dictionary of function name -> new location for moved
|
||||
objects
|
||||
"""
|
||||
|
||||
def __init__(self, deprmod, deprmodto=None, removals=None,
|
||||
moved=None):
|
||||
self.deprmod = deprmod
|
||||
self.deprmodto = deprmodto
|
||||
self.removals = removals
|
||||
if self.removals is not None:
|
||||
self.removals = frozenset(self.removals)
|
||||
self.moved = moved
|
||||
|
||||
# For introspection purposes.
|
||||
self.self_dir = frozenset(dir(self.__class__))
|
||||
|
||||
def __dir__(self):
|
||||
deprmodule = self._import_deprmod()
|
||||
return dir(deprmodule)
|
||||
|
||||
def __repr__(self):
|
||||
deprmodule = self._import_deprmod()
|
||||
return repr(deprmodule)
|
||||
|
||||
__str__ = __repr__
|
||||
|
||||
def __getattr__(self, name):
|
||||
if name in self.self_dir:
|
||||
return object.__getattribute__(self, name)
|
||||
|
||||
try:
|
||||
deprmodule = self._import_deprmod(self.deprmod)
|
||||
except ImportError:
|
||||
if self.deprmodto is None:
|
||||
raise
|
||||
|
||||
# a rename
|
||||
deprmodule = self._import_deprmod(self.deprmodto)
|
||||
|
||||
obj = getattr(deprmodule, name)
|
||||
|
||||
if self.removals is not None and name in self.removals:
|
||||
warnings.warn(
|
||||
"{deprmod}.{name} is deprecated and will be removed in "
|
||||
"a future version.".format(deprmod=self.deprmod, name=name),
|
||||
FutureWarning, stacklevel=2)
|
||||
elif self.moved is not None and name in self.moved:
|
||||
warnings.warn(
|
||||
"{deprmod} is deprecated and will be removed in "
|
||||
"a future version.\nYou can access {name} as {moved}".format(
|
||||
deprmod=self.deprmod,
|
||||
name=name,
|
||||
moved=self.moved[name]),
|
||||
FutureWarning, stacklevel=2)
|
||||
else:
|
||||
deprmodto = self.deprmodto
|
||||
if deprmodto is False:
|
||||
warnings.warn(
|
||||
"{deprmod}.{name} is deprecated and will be removed in "
|
||||
"a future version.".format(
|
||||
deprmod=self.deprmod, name=name),
|
||||
FutureWarning, stacklevel=2)
|
||||
else:
|
||||
if deprmodto is None:
|
||||
deprmodto = obj.__module__
|
||||
# The object is actually located in another module.
|
||||
warnings.warn(
|
||||
"{deprmod}.{name} is deprecated. Please use "
|
||||
"{deprmodto}.{name} instead.".format(
|
||||
deprmod=self.deprmod, name=name, deprmodto=deprmodto),
|
||||
FutureWarning, stacklevel=2)
|
||||
|
||||
return obj
|
||||
|
||||
def _import_deprmod(self, mod=None):
|
||||
if mod is None:
|
||||
mod = self.deprmod
|
||||
|
||||
with warnings.catch_warnings():
|
||||
warnings.filterwarnings('ignore', category=FutureWarning)
|
||||
deprmodule = importlib.import_module(mod)
|
||||
return deprmodule
|
||||
@@ -0,0 +1,206 @@
|
||||
import numpy as np
|
||||
|
||||
import pandas.compat as compat
|
||||
|
||||
import pandas as pd
|
||||
|
||||
|
||||
class TablePlotter(object):
|
||||
"""
|
||||
Layout some DataFrames in vertical/horizontal layout for explanation.
|
||||
Used in merging.rst
|
||||
"""
|
||||
|
||||
def __init__(self, cell_width=0.37, cell_height=0.25, font_size=7.5):
|
||||
self.cell_width = cell_width
|
||||
self.cell_height = cell_height
|
||||
self.font_size = font_size
|
||||
|
||||
def _shape(self, df):
|
||||
"""
|
||||
Calculate table chape considering index levels.
|
||||
"""
|
||||
|
||||
row, col = df.shape
|
||||
return row + df.columns.nlevels, col + df.index.nlevels
|
||||
|
||||
def _get_cells(self, left, right, vertical):
|
||||
"""
|
||||
Calculate appropriate figure size based on left and right data.
|
||||
"""
|
||||
|
||||
if vertical:
|
||||
# calculate required number of cells
|
||||
vcells = max(sum(self._shape(l)[0] for l in left),
|
||||
self._shape(right)[0])
|
||||
hcells = (max(self._shape(l)[1] for l in left) +
|
||||
self._shape(right)[1])
|
||||
else:
|
||||
vcells = max([self._shape(l)[0] for l in left] +
|
||||
[self._shape(right)[0]])
|
||||
hcells = sum([self._shape(l)[1] for l in left] +
|
||||
[self._shape(right)[1]])
|
||||
return hcells, vcells
|
||||
|
||||
def plot(self, left, right, labels=None, vertical=True):
|
||||
"""
|
||||
Plot left / right DataFrames in specified layout.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
left : list of DataFrames before operation is applied
|
||||
right : DataFrame of operation result
|
||||
labels : list of str to be drawn as titles of left DataFrames
|
||||
vertical : bool
|
||||
If True, use vertical layout. If False, use horizontal layout.
|
||||
"""
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.gridspec as gridspec
|
||||
|
||||
if not isinstance(left, list):
|
||||
left = [left]
|
||||
left = [self._conv(l) for l in left]
|
||||
right = self._conv(right)
|
||||
|
||||
hcells, vcells = self._get_cells(left, right, vertical)
|
||||
|
||||
if vertical:
|
||||
figsize = self.cell_width * hcells, self.cell_height * vcells
|
||||
else:
|
||||
# include margin for titles
|
||||
figsize = self.cell_width * hcells, self.cell_height * vcells
|
||||
fig = plt.figure(figsize=figsize)
|
||||
|
||||
if vertical:
|
||||
gs = gridspec.GridSpec(len(left), hcells)
|
||||
# left
|
||||
max_left_cols = max(self._shape(l)[1] for l in left)
|
||||
max_left_rows = max(self._shape(l)[0] for l in left)
|
||||
for i, (l, label) in enumerate(zip(left, labels)):
|
||||
ax = fig.add_subplot(gs[i, 0:max_left_cols])
|
||||
self._make_table(ax, l, title=label,
|
||||
height=1.0 / max_left_rows)
|
||||
# right
|
||||
ax = plt.subplot(gs[:, max_left_cols:])
|
||||
self._make_table(ax, right, title='Result', height=1.05 / vcells)
|
||||
fig.subplots_adjust(top=0.9, bottom=0.05, left=0.05, right=0.95)
|
||||
else:
|
||||
max_rows = max(self._shape(df)[0] for df in left + [right])
|
||||
height = 1.0 / np.max(max_rows)
|
||||
gs = gridspec.GridSpec(1, hcells)
|
||||
# left
|
||||
i = 0
|
||||
for l, label in zip(left, labels):
|
||||
sp = self._shape(l)
|
||||
ax = fig.add_subplot(gs[0, i:i + sp[1]])
|
||||
self._make_table(ax, l, title=label, height=height)
|
||||
i += sp[1]
|
||||
# right
|
||||
ax = plt.subplot(gs[0, i:])
|
||||
self._make_table(ax, right, title='Result', height=height)
|
||||
fig.subplots_adjust(top=0.85, bottom=0.05, left=0.05, right=0.95)
|
||||
|
||||
return fig
|
||||
|
||||
def _conv(self, data):
|
||||
"""Convert each input to appropriate for table outplot"""
|
||||
if isinstance(data, pd.Series):
|
||||
if data.name is None:
|
||||
data = data.to_frame(name='')
|
||||
else:
|
||||
data = data.to_frame()
|
||||
data = data.fillna('NaN')
|
||||
return data
|
||||
|
||||
def _insert_index(self, data):
|
||||
# insert is destructive
|
||||
data = data.copy()
|
||||
idx_nlevels = data.index.nlevels
|
||||
if idx_nlevels == 1:
|
||||
data.insert(0, 'Index', data.index)
|
||||
else:
|
||||
for i in range(idx_nlevels):
|
||||
data.insert(i, 'Index{0}'.format(i),
|
||||
data.index._get_level_values(i))
|
||||
|
||||
col_nlevels = data.columns.nlevels
|
||||
if col_nlevels > 1:
|
||||
col = data.columns._get_level_values(0)
|
||||
values = [data.columns._get_level_values(i).values
|
||||
for i in range(1, col_nlevels)]
|
||||
col_df = pd.DataFrame(values)
|
||||
data.columns = col_df.columns
|
||||
data = pd.concat([col_df, data])
|
||||
data.columns = col
|
||||
return data
|
||||
|
||||
def _make_table(self, ax, df, title, height=None):
|
||||
if df is None:
|
||||
ax.set_visible(False)
|
||||
return
|
||||
|
||||
import pandas.plotting as plotting
|
||||
|
||||
idx_nlevels = df.index.nlevels
|
||||
col_nlevels = df.columns.nlevels
|
||||
# must be convert here to get index levels for colorization
|
||||
df = self._insert_index(df)
|
||||
tb = plotting.table(ax, df, loc=9)
|
||||
tb.set_fontsize(self.font_size)
|
||||
|
||||
if height is None:
|
||||
height = 1.0 / (len(df) + 1)
|
||||
|
||||
props = tb.properties()
|
||||
for (r, c), cell in compat.iteritems(props['celld']):
|
||||
if c == -1:
|
||||
cell.set_visible(False)
|
||||
elif r < col_nlevels and c < idx_nlevels:
|
||||
cell.set_visible(False)
|
||||
elif r < col_nlevels or c < idx_nlevels:
|
||||
cell.set_facecolor('#AAAAAA')
|
||||
cell.set_height(height)
|
||||
|
||||
ax.set_title(title, size=self.font_size)
|
||||
ax.axis('off')
|
||||
|
||||
|
||||
class _WritableDoc(type):
|
||||
# Remove this when Python2 support is dropped
|
||||
# __doc__ is not mutable for new-style classes in Python2, which means
|
||||
# we can't use @Appender to share class docstrings. This can be used
|
||||
# with `add_metaclass` to make cls.__doc__ mutable.
|
||||
pass
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import matplotlib.pyplot as plt
|
||||
|
||||
p = TablePlotter()
|
||||
|
||||
df1 = pd.DataFrame({'A': [10, 11, 12],
|
||||
'B': [20, 21, 22],
|
||||
'C': [30, 31, 32]})
|
||||
df2 = pd.DataFrame({'A': [10, 12],
|
||||
'C': [30, 32]})
|
||||
|
||||
p.plot([df1, df2], pd.concat([df1, df2]),
|
||||
labels=['df1', 'df2'], vertical=True)
|
||||
plt.show()
|
||||
|
||||
df3 = pd.DataFrame({'X': [10, 12],
|
||||
'Z': [30, 32]})
|
||||
|
||||
p.plot([df1, df3], pd.concat([df1, df3], axis=1),
|
||||
labels=['df1', 'df2'], vertical=False)
|
||||
plt.show()
|
||||
|
||||
idx = pd.MultiIndex.from_tuples([(1, 'A'), (1, 'B'), (1, 'C'),
|
||||
(2, 'A'), (2, 'B'), (2, 'C')])
|
||||
col = pd.MultiIndex.from_tuples([(1, 'A'), (1, 'B')])
|
||||
df3 = pd.DataFrame({'v1': [1, 2, 3, 4, 5, 6],
|
||||
'v2': [5, 6, 7, 8, 9, 10]},
|
||||
index=idx)
|
||||
df3.columns = col
|
||||
p.plot(df3, df3, labels=['df3'])
|
||||
plt.show()
|
||||
@@ -0,0 +1,16 @@
|
||||
import contextlib
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def rewrite_exception(old_name, new_name):
|
||||
"""Rewrite the message of an exception."""
|
||||
try:
|
||||
yield
|
||||
except Exception as e:
|
||||
msg = e.args[0]
|
||||
msg = msg.replace(old_name, new_name)
|
||||
args = (msg,)
|
||||
if len(e.args) > 1:
|
||||
args = args + e.args[1:]
|
||||
e.args = args
|
||||
raise
|
||||
BIN
Binary file not shown.
@@ -0,0 +1,159 @@
|
||||
import codecs
|
||||
import importlib
|
||||
import locale
|
||||
import os
|
||||
import platform
|
||||
import struct
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
|
||||
def get_sys_info():
|
||||
"Returns system information as a dict"
|
||||
|
||||
blob = []
|
||||
|
||||
# get full commit hash
|
||||
commit = None
|
||||
if os.path.isdir(".git") and os.path.isdir("pandas"):
|
||||
try:
|
||||
pipe = subprocess.Popen('git log --format="%H" -n 1'.split(" "),
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE)
|
||||
so, serr = pipe.communicate()
|
||||
except (OSError, ValueError):
|
||||
pass
|
||||
else:
|
||||
if pipe.returncode == 0:
|
||||
commit = so
|
||||
try:
|
||||
commit = so.decode('utf-8')
|
||||
except ValueError:
|
||||
pass
|
||||
commit = commit.strip().strip('"')
|
||||
|
||||
blob.append(('commit', commit))
|
||||
|
||||
try:
|
||||
(sysname, nodename, release,
|
||||
version, machine, processor) = platform.uname()
|
||||
blob.extend([
|
||||
("python", '.'.join(map(str, sys.version_info))),
|
||||
("python-bits", struct.calcsize("P") * 8),
|
||||
("OS", "{sysname}".format(sysname=sysname)),
|
||||
("OS-release", "{release}".format(release=release)),
|
||||
# ("Version", "{version}".format(version=version)),
|
||||
("machine", "{machine}".format(machine=machine)),
|
||||
("processor", "{processor}".format(processor=processor)),
|
||||
("byteorder", "{byteorder}".format(byteorder=sys.byteorder)),
|
||||
("LC_ALL", "{lc}".format(lc=os.environ.get('LC_ALL', "None"))),
|
||||
("LANG", "{lang}".format(lang=os.environ.get('LANG', "None"))),
|
||||
("LOCALE", '.'.join(map(str, locale.getlocale()))),
|
||||
])
|
||||
except (KeyError, ValueError):
|
||||
pass
|
||||
|
||||
return blob
|
||||
|
||||
|
||||
def show_versions(as_json=False):
|
||||
sys_info = get_sys_info()
|
||||
|
||||
deps = [
|
||||
# (MODULE_NAME, f(mod) -> mod version)
|
||||
("pandas", lambda mod: mod.__version__),
|
||||
("pytest", lambda mod: mod.__version__),
|
||||
("pip", lambda mod: mod.__version__),
|
||||
("setuptools", lambda mod: mod.__version__),
|
||||
("Cython", lambda mod: mod.__version__),
|
||||
("numpy", lambda mod: mod.version.version),
|
||||
("scipy", lambda mod: mod.version.version),
|
||||
("pyarrow", lambda mod: mod.__version__),
|
||||
("xarray", lambda mod: mod.__version__),
|
||||
("IPython", lambda mod: mod.__version__),
|
||||
("sphinx", lambda mod: mod.__version__),
|
||||
("patsy", lambda mod: mod.__version__),
|
||||
("dateutil", lambda mod: mod.__version__),
|
||||
("pytz", lambda mod: mod.VERSION),
|
||||
("blosc", lambda mod: mod.__version__),
|
||||
("bottleneck", lambda mod: mod.__version__),
|
||||
("tables", lambda mod: mod.__version__),
|
||||
("numexpr", lambda mod: mod.__version__),
|
||||
("feather", lambda mod: mod.__version__),
|
||||
("matplotlib", lambda mod: mod.__version__),
|
||||
("openpyxl", lambda mod: mod.__version__),
|
||||
("xlrd", lambda mod: mod.__VERSION__),
|
||||
("xlwt", lambda mod: mod.__VERSION__),
|
||||
("xlsxwriter", lambda mod: mod.__version__),
|
||||
("lxml.etree", lambda mod: mod.__version__),
|
||||
("bs4", lambda mod: mod.__version__),
|
||||
("html5lib", lambda mod: mod.__version__),
|
||||
("sqlalchemy", lambda mod: mod.__version__),
|
||||
("pymysql", lambda mod: mod.__version__),
|
||||
("psycopg2", lambda mod: mod.__version__),
|
||||
("jinja2", lambda mod: mod.__version__),
|
||||
("s3fs", lambda mod: mod.__version__),
|
||||
("fastparquet", lambda mod: mod.__version__),
|
||||
("pandas_gbq", lambda mod: mod.__version__),
|
||||
("pandas_datareader", lambda mod: mod.__version__),
|
||||
("gcsfs", lambda mod: mod.__version__),
|
||||
]
|
||||
|
||||
deps_blob = list()
|
||||
for (modname, ver_f) in deps:
|
||||
try:
|
||||
if modname in sys.modules:
|
||||
mod = sys.modules[modname]
|
||||
else:
|
||||
mod = importlib.import_module(modname)
|
||||
ver = ver_f(mod)
|
||||
deps_blob.append((modname, ver))
|
||||
except ImportError:
|
||||
deps_blob.append((modname, None))
|
||||
|
||||
if (as_json):
|
||||
try:
|
||||
import json
|
||||
except ImportError:
|
||||
import simplejson as json
|
||||
|
||||
j = dict(system=dict(sys_info), dependencies=dict(deps_blob))
|
||||
|
||||
if as_json is True:
|
||||
print(j)
|
||||
else:
|
||||
with codecs.open(as_json, "wb", encoding='utf8') as f:
|
||||
json.dump(j, f, indent=2)
|
||||
|
||||
else:
|
||||
|
||||
print("\nINSTALLED VERSIONS")
|
||||
print("------------------")
|
||||
|
||||
for k, stat in sys_info:
|
||||
print("{k}: {stat}".format(k=k, stat=stat))
|
||||
|
||||
print("")
|
||||
for k, stat in deps_blob:
|
||||
print("{k}: {stat}".format(k=k, stat=stat))
|
||||
|
||||
|
||||
def main():
|
||||
from optparse import OptionParser
|
||||
parser = OptionParser()
|
||||
parser.add_option("-j", "--json", metavar="FILE", nargs=1,
|
||||
help="Save output as JSON into file, pass in "
|
||||
"'-' to output to stdout")
|
||||
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
if options.json == "-":
|
||||
options.json = True
|
||||
|
||||
show_versions(as_json=options.json)
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -0,0 +1,210 @@
|
||||
"""
|
||||
This module provides decorator functions which can be applied to test objects
|
||||
in order to skip those objects when certain conditions occur. A sample use case
|
||||
is to detect if the platform is missing ``matplotlib``. If so, any test objects
|
||||
which require ``matplotlib`` and decorated with ``@td.skip_if_no_mpl`` will be
|
||||
skipped by ``pytest`` during the execution of the test suite.
|
||||
|
||||
To illustrate, after importing this module:
|
||||
|
||||
import pandas.util._test_decorators as td
|
||||
|
||||
The decorators can be applied to classes:
|
||||
|
||||
@td.skip_if_some_reason
|
||||
class Foo():
|
||||
...
|
||||
|
||||
Or individual functions:
|
||||
|
||||
@td.skip_if_some_reason
|
||||
def test_foo():
|
||||
...
|
||||
|
||||
For more information, refer to the ``pytest`` documentation on ``skipif``.
|
||||
"""
|
||||
from distutils.version import LooseVersion
|
||||
import locale
|
||||
|
||||
import pytest
|
||||
|
||||
from pandas.compat import (
|
||||
PY3, import_lzma, is_platform_32bit, is_platform_windows)
|
||||
from pandas.compat.numpy import _np_version_under1p15
|
||||
|
||||
from pandas.core.computation.expressions import (
|
||||
_NUMEXPR_INSTALLED, _USE_NUMEXPR)
|
||||
|
||||
|
||||
def safe_import(mod_name, min_version=None):
|
||||
"""
|
||||
Parameters:
|
||||
-----------
|
||||
mod_name : str
|
||||
Name of the module to be imported
|
||||
min_version : str, default None
|
||||
Minimum required version of the specified mod_name
|
||||
|
||||
Returns:
|
||||
--------
|
||||
object
|
||||
The imported module if successful, or False
|
||||
"""
|
||||
try:
|
||||
mod = __import__(mod_name)
|
||||
except ImportError:
|
||||
return False
|
||||
|
||||
if not min_version:
|
||||
return mod
|
||||
else:
|
||||
import sys
|
||||
try:
|
||||
version = getattr(sys.modules[mod_name], '__version__')
|
||||
except AttributeError:
|
||||
# xlrd uses a capitalized attribute name
|
||||
version = getattr(sys.modules[mod_name], '__VERSION__')
|
||||
if version:
|
||||
from distutils.version import LooseVersion
|
||||
if LooseVersion(version) >= LooseVersion(min_version):
|
||||
return mod
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def _skip_if_no_mpl():
|
||||
mod = safe_import("matplotlib")
|
||||
if mod:
|
||||
mod.use("Agg", warn=False)
|
||||
else:
|
||||
return True
|
||||
|
||||
|
||||
def _skip_if_mpl_2_2():
|
||||
mod = safe_import("matplotlib")
|
||||
|
||||
if mod:
|
||||
v = mod.__version__
|
||||
if LooseVersion(v) > LooseVersion('2.1.2'):
|
||||
return True
|
||||
else:
|
||||
mod.use("Agg", warn=False)
|
||||
|
||||
|
||||
def _skip_if_has_locale():
|
||||
lang, _ = locale.getlocale()
|
||||
if lang is not None:
|
||||
return True
|
||||
|
||||
|
||||
def _skip_if_not_us_locale():
|
||||
lang, _ = locale.getlocale()
|
||||
if lang != 'en_US':
|
||||
return True
|
||||
|
||||
|
||||
def _skip_if_no_scipy():
|
||||
return not (safe_import('scipy.stats') and
|
||||
safe_import('scipy.sparse') and
|
||||
safe_import('scipy.interpolate') and
|
||||
safe_import('scipy.signal'))
|
||||
|
||||
|
||||
def _skip_if_no_lzma():
|
||||
try:
|
||||
import_lzma()
|
||||
except ImportError:
|
||||
return True
|
||||
|
||||
|
||||
def skip_if_no(package, min_version=None):
|
||||
"""
|
||||
Generic function to help skip test functions when required packages are not
|
||||
present on the testing system.
|
||||
|
||||
Intended for use as a decorator, this function will wrap the decorated
|
||||
function with a pytest ``skip_if`` mark. During a pytest test suite
|
||||
execution, that mark will attempt to import the specified ``package`` and
|
||||
optionally ensure it meets the ``min_version``. If the import and version
|
||||
check are unsuccessful, then the decorated function will be skipped.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
package: str
|
||||
The name of the package required by the decorated function
|
||||
min_version: str or None, default None
|
||||
Optional minimum version of the package required by the decorated
|
||||
function
|
||||
|
||||
Returns
|
||||
-------
|
||||
decorated_func: function
|
||||
The decorated function wrapped within a pytest ``skip_if`` mark
|
||||
"""
|
||||
def decorated_func(func):
|
||||
msg = "Could not import '{}'".format(package)
|
||||
if min_version:
|
||||
msg += " satisfying a min_version of {}".format(min_version)
|
||||
return pytest.mark.skipif(
|
||||
not safe_import(package, min_version=min_version), reason=msg
|
||||
)(func)
|
||||
return decorated_func
|
||||
|
||||
|
||||
skip_if_no_mpl = pytest.mark.skipif(_skip_if_no_mpl(),
|
||||
reason="Missing matplotlib dependency")
|
||||
skip_if_np_lt_115 = pytest.mark.skipif(_np_version_under1p15,
|
||||
reason="NumPy 1.15 or greater required")
|
||||
skip_if_mpl = pytest.mark.skipif(not _skip_if_no_mpl(),
|
||||
reason="matplotlib is present")
|
||||
xfail_if_mpl_2_2 = pytest.mark.xfail(_skip_if_mpl_2_2(),
|
||||
reason="matplotlib 2.2",
|
||||
strict=False)
|
||||
skip_if_32bit = pytest.mark.skipif(is_platform_32bit(),
|
||||
reason="skipping for 32 bit")
|
||||
skip_if_windows = pytest.mark.skipif(is_platform_windows(),
|
||||
reason="Running on Windows")
|
||||
skip_if_windows_python_3 = pytest.mark.skipif(is_platform_windows() and PY3,
|
||||
reason=("not used on python3/"
|
||||
"win32"))
|
||||
skip_if_has_locale = pytest.mark.skipif(_skip_if_has_locale(),
|
||||
reason="Specific locale is set {lang}"
|
||||
.format(lang=locale.getlocale()[0]))
|
||||
skip_if_not_us_locale = pytest.mark.skipif(_skip_if_not_us_locale(),
|
||||
reason="Specific locale is set "
|
||||
"{lang}".format(
|
||||
lang=locale.getlocale()[0]))
|
||||
skip_if_no_scipy = pytest.mark.skipif(_skip_if_no_scipy(),
|
||||
reason="Missing SciPy requirement")
|
||||
skip_if_no_lzma = pytest.mark.skipif(_skip_if_no_lzma(),
|
||||
reason="need backports.lzma to run")
|
||||
skip_if_no_ne = pytest.mark.skipif(not _USE_NUMEXPR,
|
||||
reason="numexpr enabled->{enabled}, "
|
||||
"installed->{installed}".format(
|
||||
enabled=_USE_NUMEXPR,
|
||||
installed=_NUMEXPR_INSTALLED))
|
||||
|
||||
|
||||
def parametrize_fixture_doc(*args):
|
||||
"""
|
||||
Intended for use as a decorator for parametrized fixture,
|
||||
this function will wrap the decorated function with a pytest
|
||||
``parametrize_fixture_doc`` mark. That mark will format
|
||||
initial fixture docstring by replacing placeholders {0}, {1} etc
|
||||
with parameters passed as arguments.
|
||||
|
||||
Parameters:
|
||||
----------
|
||||
args: iterable
|
||||
Positional arguments for docstring.
|
||||
|
||||
Returns:
|
||||
-------
|
||||
documented_fixture: function
|
||||
The decorated function wrapped within a pytest
|
||||
``parametrize_fixture_doc`` mark
|
||||
"""
|
||||
def documented_fixture(fixture):
|
||||
fixture.__doc__ = fixture.__doc__.format(*args)
|
||||
return fixture
|
||||
return documented_fixture
|
||||
@@ -0,0 +1,29 @@
|
||||
"""
|
||||
Entrypoint for testing from the top-level namespace
|
||||
"""
|
||||
import os
|
||||
import sys
|
||||
|
||||
PKG = os.path.dirname(os.path.dirname(__file__))
|
||||
|
||||
|
||||
def test(extra_args=None):
|
||||
try:
|
||||
import pytest
|
||||
except ImportError:
|
||||
raise ImportError("Need pytest>=3.0 to run tests")
|
||||
try:
|
||||
import hypothesis # noqa
|
||||
except ImportError:
|
||||
raise ImportError("Need hypothesis>=3.58 to run tests")
|
||||
cmd = ['--skip-slow', '--skip-network', '--skip-db']
|
||||
if extra_args:
|
||||
if not isinstance(extra_args, list):
|
||||
extra_args = [extra_args]
|
||||
cmd = extra_args
|
||||
cmd += [PKG]
|
||||
print("running: pytest {}".format(' '.join(cmd)))
|
||||
sys.exit(pytest.main(cmd))
|
||||
|
||||
|
||||
__all__ = ['test']
|
||||
@@ -0,0 +1,358 @@
|
||||
"""
|
||||
Module that contains many useful utilities
|
||||
for validating data or function arguments
|
||||
"""
|
||||
import warnings
|
||||
|
||||
from pandas.core.dtypes.common import is_bool
|
||||
|
||||
|
||||
def _check_arg_length(fname, args, max_fname_arg_count, compat_args):
|
||||
"""
|
||||
Checks whether 'args' has length of at most 'compat_args'. Raises
|
||||
a TypeError if that is not the case, similar to in Python when a
|
||||
function is called with too many arguments.
|
||||
|
||||
"""
|
||||
if max_fname_arg_count < 0:
|
||||
raise ValueError("'max_fname_arg_count' must be non-negative")
|
||||
|
||||
if len(args) > len(compat_args):
|
||||
max_arg_count = len(compat_args) + max_fname_arg_count
|
||||
actual_arg_count = len(args) + max_fname_arg_count
|
||||
argument = 'argument' if max_arg_count == 1 else 'arguments'
|
||||
|
||||
raise TypeError(
|
||||
"{fname}() takes at most {max_arg} {argument} "
|
||||
"({given_arg} given)".format(
|
||||
fname=fname, max_arg=max_arg_count,
|
||||
argument=argument, given_arg=actual_arg_count))
|
||||
|
||||
|
||||
def _check_for_default_values(fname, arg_val_dict, compat_args):
|
||||
"""
|
||||
Check that the keys in `arg_val_dict` are mapped to their
|
||||
default values as specified in `compat_args`.
|
||||
|
||||
Note that this function is to be called only when it has been
|
||||
checked that arg_val_dict.keys() is a subset of compat_args
|
||||
|
||||
"""
|
||||
for key in arg_val_dict:
|
||||
# try checking equality directly with '=' operator,
|
||||
# as comparison may have been overridden for the left
|
||||
# hand object
|
||||
try:
|
||||
v1 = arg_val_dict[key]
|
||||
v2 = compat_args[key]
|
||||
|
||||
# check for None-ness otherwise we could end up
|
||||
# comparing a numpy array vs None
|
||||
if (v1 is not None and v2 is None) or \
|
||||
(v1 is None and v2 is not None):
|
||||
match = False
|
||||
else:
|
||||
match = (v1 == v2)
|
||||
|
||||
if not is_bool(match):
|
||||
raise ValueError("'match' is not a boolean")
|
||||
|
||||
# could not compare them directly, so try comparison
|
||||
# using the 'is' operator
|
||||
except ValueError:
|
||||
match = (arg_val_dict[key] is compat_args[key])
|
||||
|
||||
if not match:
|
||||
raise ValueError(("the '{arg}' parameter is not "
|
||||
"supported in the pandas "
|
||||
"implementation of {fname}()".
|
||||
format(fname=fname, arg=key)))
|
||||
|
||||
|
||||
def validate_args(fname, args, max_fname_arg_count, compat_args):
|
||||
"""
|
||||
Checks whether the length of the `*args` argument passed into a function
|
||||
has at most `len(compat_args)` arguments and whether or not all of these
|
||||
elements in `args` are set to their default values.
|
||||
|
||||
fname: str
|
||||
The name of the function being passed the `*args` parameter
|
||||
|
||||
args: tuple
|
||||
The `*args` parameter passed into a function
|
||||
|
||||
max_fname_arg_count: int
|
||||
The maximum number of arguments that the function `fname`
|
||||
can accept, excluding those in `args`. Used for displaying
|
||||
appropriate error messages. Must be non-negative.
|
||||
|
||||
compat_args: OrderedDict
|
||||
A ordered dictionary of keys and their associated default values.
|
||||
In order to accommodate buggy behaviour in some versions of `numpy`,
|
||||
where a signature displayed keyword arguments but then passed those
|
||||
arguments **positionally** internally when calling downstream
|
||||
implementations, an ordered dictionary ensures that the original
|
||||
order of the keyword arguments is enforced. Note that if there is
|
||||
only one key, a generic dict can be passed in as well.
|
||||
|
||||
Raises
|
||||
------
|
||||
TypeError if `args` contains more values than there are `compat_args`
|
||||
ValueError if `args` contains values that do not correspond to those
|
||||
of the default values specified in `compat_args`
|
||||
|
||||
"""
|
||||
_check_arg_length(fname, args, max_fname_arg_count, compat_args)
|
||||
|
||||
# We do this so that we can provide a more informative
|
||||
# error message about the parameters that we are not
|
||||
# supporting in the pandas implementation of 'fname'
|
||||
kwargs = dict(zip(compat_args, args))
|
||||
_check_for_default_values(fname, kwargs, compat_args)
|
||||
|
||||
|
||||
def _check_for_invalid_keys(fname, kwargs, compat_args):
|
||||
"""
|
||||
Checks whether 'kwargs' contains any keys that are not
|
||||
in 'compat_args' and raises a TypeError if there is one.
|
||||
|
||||
"""
|
||||
# set(dict) --> set of the dictionary's keys
|
||||
diff = set(kwargs) - set(compat_args)
|
||||
|
||||
if diff:
|
||||
bad_arg = list(diff)[0]
|
||||
raise TypeError(("{fname}() got an unexpected "
|
||||
"keyword argument '{arg}'".
|
||||
format(fname=fname, arg=bad_arg)))
|
||||
|
||||
|
||||
def validate_kwargs(fname, kwargs, compat_args):
|
||||
"""
|
||||
Checks whether parameters passed to the **kwargs argument in a
|
||||
function `fname` are valid parameters as specified in `*compat_args`
|
||||
and whether or not they are set to their default values.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
fname: str
|
||||
The name of the function being passed the `**kwargs` parameter
|
||||
|
||||
kwargs: dict
|
||||
The `**kwargs` parameter passed into `fname`
|
||||
|
||||
compat_args: dict
|
||||
A dictionary of keys that `kwargs` is allowed to have and their
|
||||
associated default values
|
||||
|
||||
Raises
|
||||
------
|
||||
TypeError if `kwargs` contains keys not in `compat_args`
|
||||
ValueError if `kwargs` contains keys in `compat_args` that do not
|
||||
map to the default values specified in `compat_args`
|
||||
|
||||
"""
|
||||
kwds = kwargs.copy()
|
||||
_check_for_invalid_keys(fname, kwargs, compat_args)
|
||||
_check_for_default_values(fname, kwds, compat_args)
|
||||
|
||||
|
||||
def validate_args_and_kwargs(fname, args, kwargs,
|
||||
max_fname_arg_count,
|
||||
compat_args):
|
||||
"""
|
||||
Checks whether parameters passed to the *args and **kwargs argument in a
|
||||
function `fname` are valid parameters as specified in `*compat_args`
|
||||
and whether or not they are set to their default values.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
fname: str
|
||||
The name of the function being passed the `**kwargs` parameter
|
||||
|
||||
args: tuple
|
||||
The `*args` parameter passed into a function
|
||||
|
||||
kwargs: dict
|
||||
The `**kwargs` parameter passed into `fname`
|
||||
|
||||
max_fname_arg_count: int
|
||||
The minimum number of arguments that the function `fname`
|
||||
requires, excluding those in `args`. Used for displaying
|
||||
appropriate error messages. Must be non-negative.
|
||||
|
||||
compat_args: OrderedDict
|
||||
A ordered dictionary of keys that `kwargs` is allowed to
|
||||
have and their associated default values. Note that if there
|
||||
is only one key, a generic dict can be passed in as well.
|
||||
|
||||
Raises
|
||||
------
|
||||
TypeError if `args` contains more values than there are
|
||||
`compat_args` OR `kwargs` contains keys not in `compat_args`
|
||||
ValueError if `args` contains values not at the default value (`None`)
|
||||
`kwargs` contains keys in `compat_args` that do not map to the default
|
||||
value as specified in `compat_args`
|
||||
|
||||
See Also
|
||||
--------
|
||||
validate_args : Purely args validation.
|
||||
validate_kwargs : Purely kwargs validation.
|
||||
|
||||
"""
|
||||
# Check that the total number of arguments passed in (i.e.
|
||||
# args and kwargs) does not exceed the length of compat_args
|
||||
_check_arg_length(fname, args + tuple(kwargs.values()),
|
||||
max_fname_arg_count, compat_args)
|
||||
|
||||
# Check there is no overlap with the positional and keyword
|
||||
# arguments, similar to what is done in actual Python functions
|
||||
args_dict = dict(zip(compat_args, args))
|
||||
|
||||
for key in args_dict:
|
||||
if key in kwargs:
|
||||
raise TypeError("{fname}() got multiple values for keyword "
|
||||
"argument '{arg}'".format(fname=fname, arg=key))
|
||||
|
||||
kwargs.update(args_dict)
|
||||
validate_kwargs(fname, kwargs, compat_args)
|
||||
|
||||
|
||||
def validate_bool_kwarg(value, arg_name):
|
||||
""" Ensures that argument passed in arg_name is of type bool. """
|
||||
if not (is_bool(value) or value is None):
|
||||
raise ValueError('For argument "{arg}" expected type bool, received '
|
||||
'type {typ}.'.format(arg=arg_name,
|
||||
typ=type(value).__name__))
|
||||
return value
|
||||
|
||||
|
||||
def validate_axis_style_args(data, args, kwargs, arg_name, method_name):
|
||||
"""Argument handler for mixed index, columns / axis functions
|
||||
|
||||
In an attempt to handle both `.method(index, columns)`, and
|
||||
`.method(arg, axis=.)`, we have to do some bad things to argument
|
||||
parsing. This translates all arguments to `{index=., columns=.}` style.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
data : DataFrame or Panel
|
||||
arg : tuple
|
||||
All positional arguments from the user
|
||||
kwargs : dict
|
||||
All keyword arguments from the user
|
||||
arg_name, method_name : str
|
||||
Used for better error messages
|
||||
|
||||
Returns
|
||||
-------
|
||||
kwargs : dict
|
||||
A dictionary of keyword arguments. Doesn't modify ``kwargs``
|
||||
inplace, so update them with the return value here.
|
||||
|
||||
Examples
|
||||
--------
|
||||
>>> df._validate_axis_style_args((str.upper,), {'columns': id},
|
||||
... 'mapper', 'rename')
|
||||
{'columns': <function id>, 'index': <method 'upper' of 'str' objects>}
|
||||
|
||||
This emits a warning
|
||||
>>> df._validate_axis_style_args((str.upper, id), {},
|
||||
... 'mapper', 'rename')
|
||||
{'columns': <function id>, 'index': <method 'upper' of 'str' objects>}
|
||||
"""
|
||||
# TODO(PY3): Change to keyword-only args and remove all this
|
||||
|
||||
out = {}
|
||||
# Goal: fill 'out' with index/columns-style arguments
|
||||
# like out = {'index': foo, 'columns': bar}
|
||||
|
||||
# Start by validating for consistency
|
||||
if 'axis' in kwargs and any(x in kwargs for x in data._AXIS_NUMBERS):
|
||||
msg = "Cannot specify both 'axis' and any of 'index' or 'columns'."
|
||||
raise TypeError(msg)
|
||||
|
||||
# First fill with explicit values provided by the user...
|
||||
if arg_name in kwargs:
|
||||
if args:
|
||||
msg = ("{} got multiple values for argument "
|
||||
"'{}'".format(method_name, arg_name))
|
||||
raise TypeError(msg)
|
||||
|
||||
axis = data._get_axis_name(kwargs.get('axis', 0))
|
||||
out[axis] = kwargs[arg_name]
|
||||
|
||||
# More user-provided arguments, now from kwargs
|
||||
for k, v in kwargs.items():
|
||||
try:
|
||||
ax = data._get_axis_name(k)
|
||||
except ValueError:
|
||||
pass
|
||||
else:
|
||||
out[ax] = v
|
||||
|
||||
# All user-provided kwargs have been handled now.
|
||||
# Now we supplement with positional arguments, emitting warnings
|
||||
# when there's ambiguity and raising when there's conflicts
|
||||
|
||||
if len(args) == 0:
|
||||
pass # It's up to the function to decide if this is valid
|
||||
elif len(args) == 1:
|
||||
axis = data._get_axis_name(kwargs.get('axis', 0))
|
||||
out[axis] = args[0]
|
||||
elif len(args) == 2:
|
||||
if 'axis' in kwargs:
|
||||
# Unambiguously wrong
|
||||
msg = ("Cannot specify both 'axis' and any of 'index' "
|
||||
"or 'columns'")
|
||||
raise TypeError(msg)
|
||||
|
||||
msg = ("Interpreting call\n\t'.{method_name}(a, b)' as "
|
||||
"\n\t'.{method_name}(index=a, columns=b)'.\nUse named "
|
||||
"arguments to remove any ambiguity. In the future, using "
|
||||
"positional arguments for 'index' or 'columns' will raise "
|
||||
" a 'TypeError'.")
|
||||
warnings.warn(msg.format(method_name=method_name,), FutureWarning,
|
||||
stacklevel=4)
|
||||
out[data._AXIS_NAMES[0]] = args[0]
|
||||
out[data._AXIS_NAMES[1]] = args[1]
|
||||
else:
|
||||
msg = "Cannot specify all of '{}', 'index', 'columns'."
|
||||
raise TypeError(msg.format(arg_name))
|
||||
return out
|
||||
|
||||
|
||||
def validate_fillna_kwargs(value, method, validate_scalar_dict_value=True):
|
||||
"""Validate the keyword arguments to 'fillna'.
|
||||
|
||||
This checks that exactly one of 'value' and 'method' is specified.
|
||||
If 'method' is specified, this validates that it's a valid method.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
value, method : object
|
||||
The 'value' and 'method' keyword arguments for 'fillna'.
|
||||
validate_scalar_dict_value : bool, default True
|
||||
Whether to validate that 'value' is a scalar or dict. Specifically,
|
||||
validate that it is not a list or tuple.
|
||||
|
||||
Returns
|
||||
-------
|
||||
value, method : object
|
||||
"""
|
||||
from pandas.core.missing import clean_fill_method
|
||||
|
||||
if value is None and method is None:
|
||||
raise ValueError("Must specify a fill 'value' or 'method'.")
|
||||
elif value is None and method is not None:
|
||||
method = clean_fill_method(method)
|
||||
|
||||
elif value is not None and method is None:
|
||||
if validate_scalar_dict_value and isinstance(value, (list, tuple)):
|
||||
raise TypeError('"value" parameter must be a scalar or dict, but '
|
||||
'you passed a "{0}"'.format(type(value).__name__))
|
||||
|
||||
elif value is not None and method is not None:
|
||||
raise ValueError("Cannot specify both 'value' and 'method'.")
|
||||
|
||||
return value, method
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user