Skip to content

Commit 2337961

Browse files
add is_local_in_caller_frame impl version
1 parent 58e2446 commit 2337961

File tree

8 files changed

+90
-16
lines changed

8 files changed

+90
-16
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/*
2+
Copyright (c) 2025, Pandas Development Team
3+
All rights reserved.
4+
5+
Distributed under the terms of the BSD Simplified License.
6+
7+
The full license is in the LICENSE file, distributed with this software.
8+
*/
9+
10+
#pragma once
11+
12+
#define PY_SSIZE_T_CLEAN
13+
#include <Python.h>
14+
15+
// Return whether or not the object is a local in the caller's frame
16+
int is_local_in_caller_frame_impl(PyObject *object);

pandas/_libs/lib.pyx

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,9 @@ cdef extern from "pandas/parser/pd_parser.h":
7777

7878
PandasParser_IMPORT
7979

80+
cdef extern from "pandas/frame_utils.h":
81+
int is_local_in_caller_frame_impl(PyObject *object)
82+
8083
from pandas._libs cimport util
8184
from pandas._libs.util cimport (
8285
INT64_MAX,
@@ -3327,3 +3330,8 @@ def is_np_dtype(object dtype, str kinds=None) -> bool:
33273330
if kinds is None:
33283331
return True
33293332
return dtype.kind in kinds
3333+
3334+
3335+
def is_local_in_caller_frame(object obj):
3336+
"""Return whether or not the object is a local in the caller's frame."""
3337+
return is_local_in_caller_frame_impl(<PyObject *>obj)

pandas/_libs/meson.build

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,9 @@ libs_sources = {
9797
'sources': ['join.pyx', _khash_primitive_helper],
9898
'deps': _khash_primitive_helper_dep,
9999
},
100-
'lib': {'sources': ['lib.pyx', 'src/parser/tokenizer.c']},
100+
'lib': {
101+
'sources': ['lib.pyx', 'src/parser/tokenizer.c', 'src/frame_utils.c'],
102+
},
101103
'missing': {'sources': ['missing.pyx']},
102104
'pandas_datetime': {
103105
'sources': [

pandas/_libs/src/frame_utils.c

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
Copyright (c) 2025, Pandas Development Team
3+
All rights reserved.
4+
5+
Distributed under the terms of the BSD Simplified License.
6+
7+
The full license is in the LICENSE file, distributed with this software.
8+
*/
9+
10+
#include "pandas/frame_utils.h"
11+
12+
/* Return whether or not the object is a local in the caller's frame.*/
13+
int is_local_in_caller_frame_impl(PyObject *object) {
14+
PyFrameObject *frame = PyEval_GetFrame();
15+
if (frame == NULL) {
16+
return 0;
17+
}
18+
19+
// Get the caller's frame (skip the current frame)
20+
PyFrameObject *caller_frame = PyFrame_GetBack(frame);
21+
if (caller_frame == NULL) {
22+
return 0;
23+
}
24+
25+
// Get local variables of caller's frame and check if the object is in it
26+
PyObject *locals_dict = PyFrame_GetLocals(caller_frame);
27+
if (locals_dict == NULL) {
28+
Py_DECREF(caller_frame);
29+
return 0;
30+
}
31+
32+
int result = 0;
33+
PyObject *key, *value;
34+
Py_ssize_t pos = 0;
35+
while (PyDict_Next(locals_dict, &pos, &key, &value)) {
36+
if (object == value) {
37+
result = 1;
38+
break;
39+
}
40+
}
41+
42+
Py_DECREF(locals_dict);
43+
Py_DECREF(caller_frame);
44+
return result;
45+
}

pandas/compat/_constants.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
PYPY = platform.python_implementation() == "PyPy"
1919
WASM = (sys.platform == "emscripten") or (platform.machine() in ["wasm32", "wasm64"])
2020
ISMUSL = "musl" in (sysconfig.get_config_var("HOST_GNU_TYPE") or "")
21-
REF_COUNT = 2
22-
WARNING_CHECK_DISABLED = PY314
21+
REF_COUNT = 3 if PY314 else 2
22+
CHAINED_WARNING_DISABLED = False # PY314
2323

2424

2525
__all__ = [

pandas/core/frame.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@
5252
from pandas.compat import PYPY
5353
from pandas.compat._constants import (
5454
REF_COUNT,
55-
WARNING_CHECK_DISABLED,
5655
)
5756
from pandas.compat._optional import import_optional_dependency
5857
from pandas.compat.numpy import function as nv
@@ -4406,7 +4405,9 @@ def __setitem__(self, key, value) -> None:
44064405
# Values for 'a' and 'b' are completely ignored!
44074406
"""
44084407
if not PYPY:
4409-
if sys.getrefcount(self) <= REF_COUNT and not sys._is_local_in_caller_frame(self):
4408+
if sys.getrefcount(self) <= REF_COUNT and not lib.is_local_in_caller_frame(
4409+
self
4410+
):
44104411
warnings.warn(
44114412
_chained_assignment_msg, ChainedAssignmentError, stacklevel=2
44124413
)
@@ -9364,7 +9365,9 @@ def update(
93649365
2 3 6.0
93659366
"""
93669367
if not PYPY:
9367-
if sys.getrefcount(self) < REF_COUNT and not sys._is_local_in_caller_frame(self):
9368+
if sys.getrefcount(self) < REF_COUNT and not lib.is_local_in_caller_frame(
9369+
self
9370+
):
93689371
warnings.warn(
93699372
_chained_assignment_method_msg,
93709373
ChainedAssignmentError,

pandas/core/generic.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7084,7 +7084,7 @@ def fillna(
70847084
if not PYPY:
70857085
if sys.getrefcount(
70867086
self
7087-
) < REF_COUNT and not sys._is_local_in_caller_frame(self):
7087+
) < REF_COUNT and not lib.is_local_in_caller_frame(self):
70887088
warnings.warn(
70897089
_chained_assignment_method_msg,
70907090
ChainedAssignmentError,
@@ -7333,7 +7333,7 @@ def ffill(
73337333
if not PYPY:
73347334
if sys.getrefcount(
73357335
self
7336-
) < REF_COUNT and not sys._is_local_in_caller_frame(self):
7336+
) < REF_COUNT and not lib.is_local_in_caller_frame(self):
73377337
warnings.warn(
73387338
_chained_assignment_method_msg,
73397339
ChainedAssignmentError,
@@ -7475,7 +7475,7 @@ def bfill(
74757475
if not PYPY:
74767476
if sys.getrefcount(
74777477
self
7478-
) < REF_COUNT and not sys._is_local_in_caller_frame(self):
7478+
) < REF_COUNT and not lib.is_local_in_caller_frame(self):
74797479
warnings.warn(
74807480
_chained_assignment_method_msg,
74817481
ChainedAssignmentError,
@@ -7562,7 +7562,7 @@ def replace(
75627562
if not PYPY:
75637563
if sys.getrefcount(
75647564
self
7565-
) < REF_COUNT and not sys._is_local_in_caller_frame(self):
7565+
) < REF_COUNT and not lib.is_local_in_caller_frame(self):
75667566
warnings.warn(
75677567
_chained_assignment_method_msg,
75687568
ChainedAssignmentError,
@@ -7927,7 +7927,7 @@ def interpolate(
79277927
if not PYPY:
79287928
if sys.getrefcount(
79297929
self
7930-
) < REF_COUNT and not sys._is_local_in_caller_frame(self):
7930+
) < REF_COUNT and not lib.is_local_in_caller_frame(self):
79317931
warnings.warn(
79327932
_chained_assignment_method_msg,
79337933
ChainedAssignmentError,
@@ -8584,7 +8584,7 @@ def clip(
85848584
if not PYPY:
85858585
if sys.getrefcount(
85868586
self
8587-
) < REF_COUNT and not sys._is_local_in_caller_frame(self):
8587+
) < REF_COUNT and not lib.is_local_in_caller_frame(self):
85888588
warnings.warn(
85898589
_chained_assignment_method_msg,
85908590
ChainedAssignmentError,
@@ -10221,7 +10221,7 @@ def where(
1022110221
if not PYPY:
1022210222
if sys.getrefcount(
1022310223
self
10224-
) < REF_COUNT and not sys._is_local_in_caller_frame(self):
10224+
) < REF_COUNT and not lib.is_local_in_caller_frame(self):
1022510225
warnings.warn(
1022610226
_chained_assignment_method_msg,
1022710227
ChainedAssignmentError,
@@ -10287,7 +10287,7 @@ def mask(
1028710287
if not PYPY:
1028810288
if sys.getrefcount(
1028910289
self
10290-
) < REF_COUNT and not sys._is_local_in_caller_frame(self):
10290+
) < REF_COUNT and not lib.is_local_in_caller_frame(self):
1029110291
warnings.warn(
1029210292
_chained_assignment_method_msg,
1029310293
ChainedAssignmentError,

pandas/core/series.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,7 +1058,7 @@ def _get_value(self, label, takeable: bool = False):
10581058

10591059
def __setitem__(self, key, value) -> None:
10601060
if not PYPY:
1061-
if sys.getrefcount(self) <= REF_COUNT and not sys._is_local_in_caller_frame(
1061+
if sys.getrefcount(self) <= REF_COUNT and not lib.is_local_in_caller_frame(
10621062
self
10631063
):
10641064
warnings.warn(
@@ -3354,7 +3354,7 @@ def update(self, other: Series | Sequence | Mapping) -> None:
33543354
dtype: int64
33553355
"""
33563356
if not PYPY:
3357-
if sys.getrefcount(self) < REF_COUNT and not sys._is_local_in_caller_frame(
3357+
if sys.getrefcount(self) < REF_COUNT and not lib.is_local_in_caller_frame(
33583358
self
33593359
):
33603360
warnings.warn(

0 commit comments

Comments
 (0)