Skip to content

Commit 5b355cf

Browse files
dataxerikyehoshuadimarsky
authored andcommitted
ENH: Move SettingWithCopyError to error/__init__.py per GH27656 (pandas-dev#47151)
* ENH: Move SettingWithCopyError to error/__init__.py per GH27656 * ENH: add exception to test_exception_importable
1 parent 55e33db commit 5b355cf

File tree

13 files changed

+61
-37
lines changed

13 files changed

+61
-37
lines changed

doc/source/reference/testing.rst

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ Exceptions and warnings
4141
errors.ParserError
4242
errors.ParserWarning
4343
errors.PerformanceWarning
44+
errors.SettingWithCopyError
4445
errors.SpecificationError
4546
errors.UnsortedIndexError
4647
errors.UnsupportedFunctionCall

doc/source/user_guide/indexing.rst

+2-2
Original file line numberDiff line numberDiff line change
@@ -1885,7 +1885,7 @@ chained indexing expression, you can set the :ref:`option <options>`
18851885
``mode.chained_assignment`` to one of these values:
18861886

18871887
* ``'warn'``, the default, means a ``SettingWithCopyWarning`` is printed.
1888-
* ``'raise'`` means pandas will raise a ``SettingWithCopyException``
1888+
* ``'raise'`` means pandas will raise a ``SettingWithCopyError``
18891889
you have to deal with.
18901890
* ``None`` will suppress the warnings entirely.
18911891

@@ -1953,7 +1953,7 @@ Last, the subsequent example will **not** work at all, and so should be avoided:
19531953
>>> dfd.loc[0]['a'] = 1111
19541954
Traceback (most recent call last)
19551955
...
1956-
SettingWithCopyException:
1956+
SettingWithCopyError:
19571957
A value is trying to be set on a copy of a slice from a DataFrame.
19581958
Try using .loc[row_index,col_indexer] = value instead
19591959

doc/source/whatsnew/v1.5.0.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ Other enhancements
151151
- A :class:`errors.PerformanceWarning` is now thrown when using ``string[pyarrow]`` dtype with methods that don't dispatch to ``pyarrow.compute`` methods (:issue:`42613`)
152152
- Added ``numeric_only`` argument to :meth:`Resampler.sum`, :meth:`Resampler.prod`, :meth:`Resampler.min`, :meth:`Resampler.max`, :meth:`Resampler.first`, and :meth:`Resampler.last` (:issue:`46442`)
153153
- ``times`` argument in :class:`.ExponentialMovingWindow` now accepts ``np.timedelta64`` (:issue:`47003`)
154-
- :class:`DataError` and :class:`SpecificationError` are now exposed in ``pandas.errors`` (:issue:`27656`)
154+
- :class:`DataError`, :class:`SpecificationError`, and :class:`SettingWithCopyError` are now exposed in ``pandas.errors`` (:issue:`27656`)
155155

156156
.. ---------------------------------------------------------------------------
157157
.. _whatsnew_150.notable_bug_fixes:

pandas/core/common.py

-4
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,6 @@
5858
from pandas import Index
5959

6060

61-
class SettingWithCopyError(ValueError):
62-
pass
63-
64-
6561
class SettingWithCopyWarning(Warning):
6662
pass
6763

pandas/core/generic.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@
6969
from pandas.errors import (
7070
AbstractMethodError,
7171
InvalidIndexError,
72+
SettingWithCopyError,
7273
)
7374
from pandas.util._decorators import (
7475
deprecate_kwarg,
@@ -3949,7 +3950,7 @@ def _check_setitem_copy(self, t="setting", force=False):
39493950
)
39503951

39513952
if value == "raise":
3952-
raise com.SettingWithCopyError(t)
3953+
raise SettingWithCopyError(t)
39533954
elif value == "warn":
39543955
warnings.warn(t, com.SettingWithCopyWarning, stacklevel=find_stack_level())
39553956

pandas/errors/__init__.py

+21
Original file line numberDiff line numberDiff line change
@@ -267,3 +267,24 @@ class SpecificationError(Exception):
267267
>>> df.groupby('A').agg(['min', 'min']) # doctest: +SKIP
268268
... # SpecificationError: nested renamer is not supported
269269
"""
270+
271+
272+
class SettingWithCopyError(ValueError):
273+
"""
274+
Exception is raised when trying to set on a copied slice from a dataframe and
275+
the mode.chained_assignment is set to 'raise.' This can happen unintentionally
276+
when chained indexing.
277+
278+
For more information, see 'Evaluation order matters' on
279+
https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html
280+
281+
For more information, see 'Indexing view versus copy' on
282+
https://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html
283+
284+
Examples
285+
--------
286+
>>> pd.options.mode.chained_assignment = 'raise'
287+
>>> df = pd.DataFrame({'A': [1, 1, 1, 2, 2]}, columns=['A'])
288+
>>> df.loc[0:3]['A'] = 'a' # doctest: +SKIP
289+
... # SettingWithCopyError: A value is trying to be set on a copy of a...
290+
"""

pandas/tests/frame/indexing/test_indexing.py

+8-6
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88
import pytest
99

1010
from pandas._libs import iNaT
11-
from pandas.errors import InvalidIndexError
11+
from pandas.errors import (
12+
InvalidIndexError,
13+
SettingWithCopyError,
14+
)
1215
import pandas.util._test_decorators as td
1316

1417
from pandas.core.dtypes.common import is_integer
@@ -27,7 +30,6 @@
2730
notna,
2831
)
2932
import pandas._testing as tm
30-
import pandas.core.common as com
3133

3234
# We pass through a TypeError raised by numpy
3335
_slice_msg = "slice indices must be integers or None or have an __index__ method"
@@ -303,7 +305,7 @@ def test_setitem(self, float_frame):
303305
smaller = float_frame[:2]
304306

305307
msg = r"\nA value is trying to be set on a copy of a slice from a DataFrame"
306-
with pytest.raises(com.SettingWithCopyError, match=msg):
308+
with pytest.raises(SettingWithCopyError, match=msg):
307309
smaller["col10"] = ["1", "2"]
308310

309311
assert smaller["col10"].dtype == np.object_
@@ -546,7 +548,7 @@ def test_fancy_getitem_slice_mixed(self, float_frame, float_string_frame):
546548
assert np.shares_memory(sliced["C"]._values, float_frame["C"]._values)
547549

548550
msg = r"\nA value is trying to be set on a copy of a slice from a DataFrame"
549-
with pytest.raises(com.SettingWithCopyError, match=msg):
551+
with pytest.raises(SettingWithCopyError, match=msg):
550552
sliced.loc[:, "C"] = 4.0
551553

552554
assert (float_frame["C"] == 4).all()
@@ -1003,7 +1005,7 @@ def test_iloc_row_slice_view(self, using_array_manager):
10031005
assert np.shares_memory(df[2], subset[2])
10041006

10051007
msg = r"\nA value is trying to be set on a copy of a slice from a DataFrame"
1006-
with pytest.raises(com.SettingWithCopyError, match=msg):
1008+
with pytest.raises(SettingWithCopyError, match=msg):
10071009
subset.loc[:, 2] = 0.0
10081010

10091011
exp_col = original[2].copy()
@@ -1046,7 +1048,7 @@ def test_iloc_col_slice_view(self, using_array_manager):
10461048

10471049
# and that we are setting a copy
10481050
msg = r"\nA value is trying to be set on a copy of a slice from a DataFrame"
1049-
with pytest.raises(com.SettingWithCopyError, match=msg):
1051+
with pytest.raises(SettingWithCopyError, match=msg):
10501052
subset.loc[:, 8] = 0.0
10511053

10521054
assert (df[8] == 0).all()

pandas/tests/frame/indexing/test_xs.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
import numpy as np
44
import pytest
55

6+
from pandas.errors import SettingWithCopyError
7+
68
from pandas import (
79
DataFrame,
810
Index,
@@ -12,7 +14,6 @@
1214
concat,
1315
)
1416
import pandas._testing as tm
15-
import pandas.core.common as com
1617

1718
from pandas.tseries.offsets import BDay
1819

@@ -120,7 +121,7 @@ def test_xs_view(self, using_array_manager):
120121
# INFO(ArrayManager) with ArrayManager getting a row as a view is
121122
# not possible
122123
msg = r"\nA value is trying to be set on a copy of a slice from a DataFrame"
123-
with pytest.raises(com.SettingWithCopyError, match=msg):
124+
with pytest.raises(SettingWithCopyError, match=msg):
124125
dm.xs(2)[:] = 20
125126
assert not (dm.xs(2) == 20).any()
126127
else:
@@ -183,7 +184,7 @@ def test_xs_setting_with_copy_error(self, multiindex_dataframe_random_data):
183184
# setting this will give a SettingWithCopyError
184185
# as we are trying to write a view
185186
msg = "A value is trying to be set on a copy of a slice from a DataFrame"
186-
with pytest.raises(com.SettingWithCopyError, match=msg):
187+
with pytest.raises(SettingWithCopyError, match=msg):
187188
result[:] = 10
188189

189190
def test_xs_setting_with_copy_error_multiple(self, four_level_index_dataframe):
@@ -194,7 +195,7 @@ def test_xs_setting_with_copy_error_multiple(self, four_level_index_dataframe):
194195
# setting this will give a SettingWithCopyError
195196
# as we are trying to write a view
196197
msg = "A value is trying to be set on a copy of a slice from a DataFrame"
197-
with pytest.raises(com.SettingWithCopyError, match=msg):
198+
with pytest.raises(SettingWithCopyError, match=msg):
198199
result[:] = 10
199200

200201
@pytest.mark.parametrize("key, level", [("one", "second"), (["one"], ["second"])])

pandas/tests/indexing/multiindex/test_chaining_and_caching.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import numpy as np
22
import pytest
33

4+
from pandas.errors import SettingWithCopyError
45
import pandas.util._test_decorators as td
56

67
from pandas import (
@@ -9,7 +10,6 @@
910
Series,
1011
)
1112
import pandas._testing as tm
12-
import pandas.core.common as com
1313

1414

1515
def test_detect_chained_assignment():
@@ -30,7 +30,7 @@ def test_detect_chained_assignment():
3030
zed = DataFrame(events, index=["a", "b"], columns=multiind)
3131

3232
msg = "A value is trying to be set on a copy of a slice from a DataFrame"
33-
with pytest.raises(com.SettingWithCopyError, match=msg):
33+
with pytest.raises(SettingWithCopyError, match=msg):
3434
zed["eyes"]["right"].fillna(value=555, inplace=True)
3535

3636

pandas/tests/indexing/multiindex/test_setitem.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import numpy as np
22
import pytest
33

4+
from pandas.errors import SettingWithCopyError
45
import pandas.util._test_decorators as td
56

67
import pandas as pd
@@ -14,7 +15,6 @@
1415
notna,
1516
)
1617
import pandas._testing as tm
17-
import pandas.core.common as com
1818

1919

2020
def assert_equal(a, b):
@@ -491,7 +491,7 @@ def test_frame_setitem_copy_raises(multiindex_dataframe_random_data):
491491
# will raise/warn as its chained assignment
492492
df = multiindex_dataframe_random_data.T
493493
msg = "A value is trying to be set on a copy of a slice from a DataFrame"
494-
with pytest.raises(com.SettingWithCopyError, match=msg):
494+
with pytest.raises(SettingWithCopyError, match=msg):
495495
df["foo"]["one"] = 2
496496

497497

@@ -500,7 +500,7 @@ def test_frame_setitem_copy_no_write(multiindex_dataframe_random_data):
500500
expected = frame
501501
df = frame.copy()
502502
msg = "A value is trying to be set on a copy of a slice from a DataFrame"
503-
with pytest.raises(com.SettingWithCopyError, match=msg):
503+
with pytest.raises(SettingWithCopyError, match=msg):
504504
df["foo"]["one"] = 2
505505

506506
result = df

pandas/tests/indexing/test_chaining_and_caching.py

+13-12
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import numpy as np
44
import pytest
55

6+
from pandas.errors import SettingWithCopyError
67
import pandas.util._test_decorators as td
78

89
import pandas as pd
@@ -182,10 +183,10 @@ def test_detect_chained_assignment_raises(self, using_array_manager):
182183
assert df._is_copy is None
183184

184185
if not using_array_manager:
185-
with pytest.raises(com.SettingWithCopyError, match=msg):
186+
with pytest.raises(SettingWithCopyError, match=msg):
186187
df["A"][0] = -5
187188

188-
with pytest.raises(com.SettingWithCopyError, match=msg):
189+
with pytest.raises(SettingWithCopyError, match=msg):
189190
df["A"][1] = np.nan
190191

191192
assert df["A"]._is_copy is None
@@ -210,7 +211,7 @@ def test_detect_chained_assignment_fails(self):
210211
}
211212
)
212213

213-
with pytest.raises(com.SettingWithCopyError, match=msg):
214+
with pytest.raises(SettingWithCopyError, match=msg):
214215
df.loc[0]["A"] = -5
215216

216217
@pytest.mark.arm_slow
@@ -225,7 +226,7 @@ def test_detect_chained_assignment_doc_example(self):
225226
)
226227
assert df._is_copy is None
227228

228-
with pytest.raises(com.SettingWithCopyError, match=msg):
229+
with pytest.raises(SettingWithCopyError, match=msg):
229230
indexer = df.a.str.startswith("o")
230231
df[indexer]["c"] = 42
231232

@@ -235,11 +236,11 @@ def test_detect_chained_assignment_object_dtype(self, using_array_manager):
235236
expected = DataFrame({"A": [111, "bbb", "ccc"], "B": [1, 2, 3]})
236237
df = DataFrame({"A": ["aaa", "bbb", "ccc"], "B": [1, 2, 3]})
237238

238-
with pytest.raises(com.SettingWithCopyError, match=msg):
239+
with pytest.raises(SettingWithCopyError, match=msg):
239240
df.loc[0]["A"] = 111
240241

241242
if not using_array_manager:
242-
with pytest.raises(com.SettingWithCopyError, match=msg):
243+
with pytest.raises(SettingWithCopyError, match=msg):
243244
df["A"][0] = 111
244245

245246
df.loc[0, "A"] = 111
@@ -360,7 +361,7 @@ def test_detect_chained_assignment_undefined_column(self):
360361
df = DataFrame(np.arange(0, 9), columns=["count"])
361362
df["group"] = "b"
362363

363-
with pytest.raises(com.SettingWithCopyError, match=msg):
364+
with pytest.raises(SettingWithCopyError, match=msg):
364365
df.iloc[0:5]["group"] = "a"
365366

366367
@pytest.mark.arm_slow
@@ -376,14 +377,14 @@ def test_detect_chained_assignment_changing_dtype(self, using_array_manager):
376377
}
377378
)
378379

379-
with pytest.raises(com.SettingWithCopyError, match=msg):
380+
with pytest.raises(SettingWithCopyError, match=msg):
380381
df.loc[2]["D"] = "foo"
381382

382-
with pytest.raises(com.SettingWithCopyError, match=msg):
383+
with pytest.raises(SettingWithCopyError, match=msg):
383384
df.loc[2]["C"] = "foo"
384385

385386
if not using_array_manager:
386-
with pytest.raises(com.SettingWithCopyError, match=msg):
387+
with pytest.raises(SettingWithCopyError, match=msg):
387388
df["C"][2] = "foo"
388389
else:
389390
# INFO(ArrayManager) for ArrayManager it doesn't matter if it's
@@ -399,7 +400,7 @@ def test_setting_with_copy_bug(self):
399400
)
400401
mask = pd.isna(df.c)
401402

402-
with pytest.raises(com.SettingWithCopyError, match=msg):
403+
with pytest.raises(SettingWithCopyError, match=msg):
403404
df[["c"]][mask] = df[["b"]][mask]
404405

405406
def test_setting_with_copy_bug_no_warning(self):
@@ -418,7 +419,7 @@ def test_detect_chained_assignment_warnings_errors(self):
418419
df.loc[0]["A"] = 111
419420

420421
with option_context("chained_assignment", "raise"):
421-
with pytest.raises(com.SettingWithCopyError, match=msg):
422+
with pytest.raises(SettingWithCopyError, match=msg):
422423
df.loc[0]["A"] = 111
423424

424425
def test_detect_chained_assignment_warnings_filter_and_dupe_cols(self):

pandas/tests/series/accessors/test_dt_accessor.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import pytz
1313

1414
from pandas._libs.tslibs.timezones import maybe_get_tz
15+
from pandas.errors import SettingWithCopyError
1516

1617
from pandas.core.dtypes.common import (
1718
is_integer_dtype,
@@ -37,7 +38,6 @@
3738
PeriodArray,
3839
TimedeltaArray,
3940
)
40-
import pandas.core.common as com
4141

4242
ok_for_period = PeriodArray._datetimelike_ops
4343
ok_for_period_methods = ["strftime", "to_timestamp", "asfreq"]
@@ -288,7 +288,7 @@ def test_dt_accessor_not_writeable(self):
288288
# trying to set a copy
289289
msg = "modifications to a property of a datetimelike.+not supported"
290290
with pd.option_context("chained_assignment", "raise"):
291-
with pytest.raises(com.SettingWithCopyError, match=msg):
291+
with pytest.raises(SettingWithCopyError, match=msg):
292292
ser.dt.hour[0] = 5
293293

294294
@pytest.mark.parametrize(

pandas/tests/test_errors.py

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
"NumbaUtilError",
2222
"DataError",
2323
"SpecificationError",
24+
"SettingWithCopyError",
2425
],
2526
)
2627
def test_exception_importable(exc):

0 commit comments

Comments
 (0)