Skip to content

Enforce boolean types #14318

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/source/whatsnew/v0.20.0.txt
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ Other API Changes

- ``pd.read_csv()`` will now issue a ``ParserWarning`` whenever there are conflicting values provided by the ``dialect`` parameter and the user (:issue:`14898`)
- ``pd.read_csv()`` will now raise a ``ValueError`` for the C engine if the quote character is larger than than one byte (:issue:`11592`)
- ``inplace`` arguments now require a boolean value, else a ``ValueError`` is thrown (:issue:`14189`)

.. _whatsnew_0200.deprecations:

Expand Down
2 changes: 2 additions & 0 deletions pandas/computation/eval.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from pandas.computation.scope import _ensure_scope
from pandas.compat import string_types
from pandas.computation.engines import _engines
from pandas.util.validators import validate_bool_kwarg


def _check_engine(engine):
Expand Down Expand Up @@ -231,6 +232,7 @@ def eval(expr, parser='pandas', engine=None, truediv=True,
pandas.DataFrame.query
pandas.DataFrame.eval
"""
inplace = validate_bool_kwarg(inplace, 'inplace')
first_expr = True
if isinstance(expr, string_types):
_check_expression(expr)
Expand Down
9 changes: 9 additions & 0 deletions pandas/computation/tests/test_eval.py
Original file line number Diff line number Diff line change
Expand Up @@ -1970,6 +1970,15 @@ def test_negate_lt_eq_le():
for engine, parser in product(_engines, expr._parsers):
yield check_negate_lt_eq_le, engine, parser

class TestValidate(tm.TestCase):

def test_validate_bool_args(self):
invalid_values = [1, "True", [1,2,3], 5.0]

for value in invalid_values:
with self.assertRaises(ValueError):
pd.eval("2+2", inplace=value)


if __name__ == '__main__':
nose.runmodule(argv=[__file__, '-vvs', '-x', '--pdb', '--pdb-failure'],
Expand Down
2 changes: 2 additions & 0 deletions pandas/core/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from pandas.types.missing import isnull
from pandas.types.generic import ABCDataFrame, ABCSeries, ABCIndexClass
from pandas.types.common import is_object_dtype, is_list_like, is_scalar
from pandas.util.validators import validate_bool_kwarg

from pandas.core import common as com
import pandas.core.nanops as nanops
Expand Down Expand Up @@ -1178,6 +1179,7 @@ def searchsorted(self, value, side='left', sorter=None):
False: 'first'})
@Appender(_shared_docs['drop_duplicates'] % _indexops_doc_kwargs)
def drop_duplicates(self, keep='first', inplace=False):
inplace = validate_bool_kwarg(inplace, 'inplace')
if isinstance(self, ABCIndexClass):
if self.is_unique:
return self._shallow_copy()
Expand Down
11 changes: 11 additions & 0 deletions pandas/core/categorical.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
deprecate_kwarg, Substitution)

from pandas.util.terminal import get_terminal_size
from pandas.util.validators import validate_bool_kwarg
from pandas.core.config import get_option


Expand Down Expand Up @@ -615,6 +616,7 @@ def set_ordered(self, value, inplace=False):
Whether or not to set the ordered attribute inplace or return a copy
of this categorical with ordered set to the value
"""
inplace = validate_bool_kwarg(inplace, 'inplace')
self._validate_ordered(value)
cat = self if inplace else self.copy()
cat._ordered = value
Expand All @@ -631,6 +633,7 @@ def as_ordered(self, inplace=False):
Whether or not to set the ordered attribute inplace or return a copy
of this categorical with ordered set to True
"""
inplace = validate_bool_kwarg(inplace, 'inplace')
return self.set_ordered(True, inplace=inplace)

def as_unordered(self, inplace=False):
Expand All @@ -643,6 +646,7 @@ def as_unordered(self, inplace=False):
Whether or not to set the ordered attribute inplace or return a copy
of this categorical with ordered set to False
"""
inplace = validate_bool_kwarg(inplace, 'inplace')
return self.set_ordered(False, inplace=inplace)

def _get_ordered(self):
Expand Down Expand Up @@ -702,6 +706,7 @@ def set_categories(self, new_categories, ordered=None, rename=False,
remove_categories
remove_unused_categories
"""
inplace = validate_bool_kwarg(inplace, 'inplace')
new_categories = self._validate_categories(new_categories)
cat = self if inplace else self.copy()
if rename:
Expand Down Expand Up @@ -754,6 +759,7 @@ def rename_categories(self, new_categories, inplace=False):
remove_unused_categories
set_categories
"""
inplace = validate_bool_kwarg(inplace, 'inplace')
cat = self if inplace else self.copy()
cat.categories = new_categories
if not inplace:
Expand Down Expand Up @@ -794,6 +800,7 @@ def reorder_categories(self, new_categories, ordered=None, inplace=False):
remove_unused_categories
set_categories
"""
inplace = validate_bool_kwarg(inplace, 'inplace')
if set(self._categories) != set(new_categories):
raise ValueError("items in new_categories are not the same as in "
"old categories")
Expand Down Expand Up @@ -832,6 +839,7 @@ def add_categories(self, new_categories, inplace=False):
remove_unused_categories
set_categories
"""
inplace = validate_bool_kwarg(inplace, 'inplace')
if not is_list_like(new_categories):
new_categories = [new_categories]
already_included = set(new_categories) & set(self._categories)
Expand Down Expand Up @@ -877,6 +885,7 @@ def remove_categories(self, removals, inplace=False):
remove_unused_categories
set_categories
"""
inplace = validate_bool_kwarg(inplace, 'inplace')
if not is_list_like(removals):
removals = [removals]

Expand Down Expand Up @@ -917,6 +926,7 @@ def remove_unused_categories(self, inplace=False):
remove_categories
set_categories
"""
inplace = validate_bool_kwarg(inplace, 'inplace')
cat = self if inplace else self.copy()
idx, inv = np.unique(cat._codes, return_inverse=True)

Expand Down Expand Up @@ -1322,6 +1332,7 @@ def sort_values(self, inplace=False, ascending=True, na_position='last'):
[NaN, NaN, 5.0, 2.0, 2.0]
Categories (2, int64): [2, 5]
"""
inplace = validate_bool_kwarg(inplace, 'inplace')
if na_position not in ['last', 'first']:
raise ValueError('invalid na_position: {!r}'.format(na_position))

Expand Down
14 changes: 10 additions & 4 deletions pandas/core/frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,7 @@
import numpy as np
import numpy.ma as ma

from pandas.types.cast import (_maybe_upcast,
_infer_dtype_from_scalar,
from pandas.types.cast import (_maybe_upcast, _infer_dtype_from_scalar,
_possibly_cast_to_datetime,
_possibly_infer_to_datetimelike,
_possibly_convert_platform,
Expand Down Expand Up @@ -79,6 +78,7 @@
from pandas import compat
from pandas.compat.numpy import function as nv
from pandas.util.decorators import deprecate_kwarg, Appender, Substitution
from pandas.util.validators import validate_bool_kwarg

from pandas.tseries.period import PeriodIndex
from pandas.tseries.index import DatetimeIndex
Expand Down Expand Up @@ -2164,6 +2164,7 @@ def query(self, expr, inplace=False, **kwargs):
>>> df.query('a > b')
>>> df[df.a > df.b] # same result as the previous expression
"""
inplace = validate_bool_kwarg(inplace, 'inplace')
if not isinstance(expr, compat.string_types):
msg = "expr must be a string to be evaluated, {0} given"
raise ValueError(msg.format(type(expr)))
Expand Down Expand Up @@ -2230,6 +2231,7 @@ def eval(self, expr, inplace=None, **kwargs):
>>> df.eval('a + b')
>>> df.eval('c = a + b')
"""
inplace = validate_bool_kwarg(inplace, 'inplace')
resolvers = kwargs.pop('resolvers', None)
kwargs['level'] = kwargs.pop('level', 0) + 1
if resolvers is None:
Expand Down Expand Up @@ -2843,6 +2845,7 @@ def set_index(self, keys, drop=True, append=False, inplace=False,
-------
dataframe : DataFrame
"""
inplace = validate_bool_kwarg(inplace, 'inplace')
if not isinstance(keys, list):
keys = [keys]

Expand Down Expand Up @@ -2935,6 +2938,7 @@ def reset_index(self, level=None, drop=False, inplace=False, col_level=0,
-------
resetted : DataFrame
"""
inplace = validate_bool_kwarg(inplace, 'inplace')
if inplace:
new_obj = self
else:
Expand Down Expand Up @@ -3039,6 +3043,7 @@ def dropna(self, axis=0, how='any', thresh=None, subset=None,
-------
dropped : DataFrame
"""
inplace = validate_bool_kwarg(inplace, 'inplace')
if isinstance(axis, (tuple, list)):
result = self
for ax in axis:
Expand Down Expand Up @@ -3102,6 +3107,7 @@ def drop_duplicates(self, subset=None, keep='first', inplace=False):
-------
deduplicated : DataFrame
"""
inplace = validate_bool_kwarg(inplace, 'inplace')
duplicated = self.duplicated(subset, keep=keep)

if inplace:
Expand Down Expand Up @@ -3163,7 +3169,7 @@ def f(vals):
@Appender(_shared_docs['sort_values'] % _shared_doc_kwargs)
def sort_values(self, by, axis=0, ascending=True, inplace=False,
kind='quicksort', na_position='last'):

inplace = validate_bool_kwarg(inplace, 'inplace')
axis = self._get_axis_number(axis)
other_axis = 0 if axis == 1 else 1

Expand Down Expand Up @@ -3274,7 +3280,7 @@ def sort(self, columns=None, axis=0, ascending=True, inplace=False,
def sort_index(self, axis=0, level=None, ascending=True, inplace=False,
kind='quicksort', na_position='last', sort_remaining=True,
by=None):

inplace = validate_bool_kwarg(inplace, 'inplace')
# 10726
if by is not None:
warnings.warn("by argument to sort_index is deprecated, pls use "
Expand Down
10 changes: 10 additions & 0 deletions pandas/core/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
isidentifier, set_function_name)
import pandas.core.nanops as nanops
from pandas.util.decorators import Appender, Substitution, deprecate_kwarg
from pandas.util.validators import validate_bool_kwarg
from pandas.core import config

# goal is to be able to define the docs close to function, while still being
Expand Down Expand Up @@ -733,6 +734,7 @@ def rename_axis(self, mapper, axis=0, copy=True, inplace=False):
1 2 5
2 3 6
"""
inplace = validate_bool_kwarg(inplace, 'inplace')
non_mapper = is_scalar(mapper) or (is_list_like(mapper) and not
is_dict_like(mapper))
if non_mapper:
Expand Down Expand Up @@ -1950,6 +1952,7 @@ def drop(self, labels, axis=0, level=None, inplace=False, errors='raise'):
-------
dropped : type of caller
"""
inplace = validate_bool_kwarg(inplace, 'inplace')
axis = self._get_axis_number(axis)
axis_name = self._get_axis_name(axis)
axis, axis_ = self._get_axis(axis), axis
Expand Down Expand Up @@ -2099,6 +2102,7 @@ def sort_values(self, by, axis=0, ascending=True, inplace=False,
@Appender(_shared_docs['sort_index'] % dict(axes="axes", klass="NDFrame"))
def sort_index(self, axis=0, level=None, ascending=True, inplace=False,
kind='quicksort', na_position='last', sort_remaining=True):
inplace = validate_bool_kwarg(inplace, 'inplace')
axis = self._get_axis_number(axis)
axis_name = self._get_axis_name(axis)
labels = self._get_axis(axis)
Expand Down Expand Up @@ -2872,6 +2876,7 @@ def consolidate(self, inplace=False):
-------
consolidated : type of caller
"""
inplace = validate_bool_kwarg(inplace, 'inplace')
if inplace:
self._consolidate_inplace()
else:
Expand Down Expand Up @@ -3267,6 +3272,7 @@ def convert_objects(self, convert_dates=True, convert_numeric=False,
@Appender(_shared_docs['fillna'] % _shared_doc_kwargs)
def fillna(self, value=None, method=None, axis=None, inplace=False,
limit=None, downcast=None):
inplace = validate_bool_kwarg(inplace, 'inplace')
if isinstance(value, (list, tuple)):
raise TypeError('"value" parameter must be a scalar or dict, but '
'you passed a "{0}"'.format(type(value).__name__))
Expand Down Expand Up @@ -3479,6 +3485,7 @@ def replace(self, to_replace=None, value=None, inplace=False, limit=None,
and play with this method to gain intuition about how it works.

"""
inplace = validate_bool_kwarg(inplace, 'inplace')
if not is_bool(regex) and to_replace is not None:
raise AssertionError("'to_replace' must be 'None' if 'regex' is "
"not a bool")
Expand Down Expand Up @@ -3714,6 +3721,7 @@ def interpolate(self, method='linear', axis=0, limit=None, inplace=False,
"""
Interpolate values according to different methods.
"""
inplace = validate_bool_kwarg(inplace, 'inplace')

if self.ndim > 2:
raise NotImplementedError("Interpolate has not been implemented "
Expand Down Expand Up @@ -4627,6 +4635,7 @@ def _where(self, cond, other=np.nan, inplace=False, axis=None, level=None,
Equivalent to public method `where`, except that `other` is not
applied as a function even if callable. Used in __setitem__.
"""
inplace = validate_bool_kwarg(inplace, 'inplace')

cond = com._apply_if_callable(cond, self)

Expand Down Expand Up @@ -4894,6 +4903,7 @@ def where(self, cond, other=np.nan, inplace=False, axis=None, level=None,
def mask(self, cond, other=np.nan, inplace=False, axis=None, level=None,
try_cast=False, raise_on_error=True):

inplace = validate_bool_kwarg(inplace, 'inplace')
cond = com._apply_if_callable(cond, self)

return self.where(~cond, other=other, inplace=inplace, axis=axis,
Expand Down
16 changes: 16 additions & 0 deletions pandas/core/internals.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
import pandas.tslib as tslib
import pandas.computation.expressions as expressions
from pandas.util.decorators import cache_readonly
from pandas.util.validators import validate_bool_kwarg

from pandas.tslib import Timedelta
from pandas import compat, _np_version_under1p9
Expand Down Expand Up @@ -360,6 +361,7 @@ def fillna(self, value, limit=None, inplace=False, downcast=None,
""" fillna on the block with the value. If we fail, then convert to
ObjectBlock and try again
"""
inplace = validate_bool_kwarg(inplace, 'inplace')

if not self._can_hold_na:
if inplace:
Expand Down Expand Up @@ -626,6 +628,7 @@ def replace(self, to_replace, value, inplace=False, filter=None,
compatibility.
"""

inplace = validate_bool_kwarg(inplace, 'inplace')
original_to_replace = to_replace
mask = isnull(self.values)
# try to replace, if we raise an error, convert to ObjectBlock and
Expand Down Expand Up @@ -897,6 +900,9 @@ def interpolate(self, method='pad', axis=0, index=None, values=None,
inplace=False, limit=None, limit_direction='forward',
fill_value=None, coerce=False, downcast=None, mgr=None,
**kwargs):

inplace = validate_bool_kwarg(inplace, 'inplace')

def check_int_bool(self, inplace):
# Only FloatBlocks will contain NaNs.
# timedelta subclasses IntBlock
Expand Down Expand Up @@ -944,6 +950,8 @@ def _interpolate_with_fill(self, method='pad', axis=0, inplace=False,
downcast=None, mgr=None):
""" fillna but using the interpolate machinery """

inplace = validate_bool_kwarg(inplace, 'inplace')

# if we are coercing, then don't force the conversion
# if the block can't hold the type
if coerce:
Expand All @@ -970,6 +978,7 @@ def _interpolate(self, method=None, index=None, values=None,
mgr=None, **kwargs):
""" interpolate using scipy wrappers """

inplace = validate_bool_kwarg(inplace, 'inplace')
data = self.values if inplace else self.values.copy()

# only deal with floats
Expand Down Expand Up @@ -1514,6 +1523,7 @@ def putmask(self, mask, new, align=True, inplace=False, axis=0,
-------
a new block(s), the result of the putmask
"""
inplace = validate_bool_kwarg(inplace, 'inplace')

# use block's copy logic.
# .values may be an Index which does shallow copy by default
Expand Down Expand Up @@ -1801,6 +1811,7 @@ def should_store(self, value):

def replace(self, to_replace, value, inplace=False, filter=None,
regex=False, convert=True, mgr=None):
inplace = validate_bool_kwarg(inplace, 'inplace')
to_replace_values = np.atleast_1d(to_replace)
if not np.can_cast(to_replace_values, bool):
return self
Expand Down Expand Up @@ -1982,6 +1993,9 @@ def replace(self, to_replace, value, inplace=False, filter=None,

def _replace_single(self, to_replace, value, inplace=False, filter=None,
regex=False, convert=True, mgr=None):

inplace = validate_bool_kwarg(inplace, 'inplace')

# to_replace is regex compilable
to_rep_re = regex and is_re_compilable(to_replace)

Expand Down Expand Up @@ -3205,6 +3219,8 @@ def replace_list(self, src_list, dest_list, inplace=False, regex=False,
mgr=None):
""" do a list replace """

inplace = validate_bool_kwarg(inplace, 'inplace')

if mgr is None:
mgr = self

Expand Down
Loading