-
-
Notifications
You must be signed in to change notification settings - Fork 18.3k
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
BUG: ChainedAssignmentError for CoW not working for chained inplace methods when passing *args
or **kwargs
#56456
Comments
Some more details on the inner Python interpreter details about why this is happening: when calling the function with Example disassembly (opcodes) for Python 3.10 vs 3.11 for the above fillna callPython 3.10 (both cases have ref count of 4):
Python 3.11:
We actually noticed this different ref count for the standard case, and therefore use a different threshold for Python 3.11+: pandas/pandas/compat/_constants.py Line 21 in 400ae74
But that means that for the args/kwargs use case, we incorrectly don't trigger a warning. If we want, we probably would be able to inspect the stack to try to figure out whether the method was called with args/kwargs or not, if we are OK with paying the code complexity for this and the runtime overhead. Short-term we probably won't include this, but if it turns out to be a more common case, we can always try that in the future. def _is_using_args_kwargs(code_object, method):
# simple case of df.method(..) without args/kwargs
for instr in dis.get_instructions(code_object):
if instr.opname == "LOAD_METHOD" and instr.argval == method:
print("we have LOAD_METHOD (fillna)")
return False
# fallback for more complex cases
instructions = list(dis.get_instructions(code_object))
instr_call_ex = [i for i, instr in enumerate(instructions) if instr.opname == "CALL_FUNCTION_EX"]
if not instr_call_ex:
print("we have no CALL_FUNCTION_EX")
return False
method_with_ex = False
for case in instr_call_ex:
for i in range(case, 0, -1):
if instructions[i].opname == "LOAD_ATTR":
method_with_ex = instructions[i].argval == method
if method_with_ex:
print("we have CALL_FUNCTION_EX with prepending LOAD_ATTR (fillna)")
break
if method_with_ex:
return True
return method_with_ex and this works as
For the actual method, it could be integrated with something like: --- a/pandas/core/generic.py
+++ b/pandas/core/generic.py
@@ -6,6 +6,7 @@ from copy import deepcopy
import datetime as dt
from functools import partial
import gc
+import inspect
from json import loads
import operator
import pickle
@@ -7304,11 +7306,20 @@ class NDFrame(PandasObject, indexing.IndexingMixin):
and self._is_view_after_cow_rules()
):
ctr = sys.getrefcount(self)
ref_count = REF_COUNT
if isinstance(self, ABCSeries) and _check_cacher(self):
# see https://github.com/pandas-dev/pandas/pull/56060#discussion_r1399245221
ref_count += 1
+ if PY311:
+ calling_frame = inspect.currentframe().f_back
+ if _is_using_args_kwargs(calling_frame.f_code):
+ ref_count += 1
if ctr <= ref_count:
warnings.warn(
_chained_assignment_warning_method_msg,
FutureWarning, |
While working on #56402 (ensuring full test coverage for the warning we raise when you do chained inplace methods), we ran into another corner case where the refcount differs, and mixes up the refcount-based inference about whether we are being called in a "chained" context and thus have to raise the ChainedAssignment warning.
(a previous case we discovered was when the setitem call is coming from cython code: #51315)
Specifically, when passing
*args
or**kwargs
to the inplace method call, it takes a different execution path in the Python interpreter compared to calling it with manually specified arguments. And starting from Python 3.11, this other execution path gives one reference less.Example (with CoW, the
df
will never be updated with such chained method call):That warning works for all tested Python versions. However, when passing the
args
orkwargs
, the warning doesn't work on Python >= 3.11:For now we decided to punt on this specific corner case, but creating this issue to we keep track of what we learned and have something to reference to when this might come up later.
The text was updated successfully, but these errors were encountered: