@@ -88,54 +88,37 @@ PyBufferProcs stolenbuf_as_buffer = {
88
88
89
89
#endif /* COMPILING_IN_PY2 */
90
90
91
- static PyObject *
92
- stolenbuf_new (PyObject * self , PyObject * args , PyObject * kwargs )
93
- {
94
- stolenbufobject * ret ;
95
- PyObject * bytes_rvalue ;
96
-
97
- if (kwargs && PyDict_Size (kwargs )) {
98
- PyErr_SetString (PyExc_TypeError ,
99
- "stolenbuf does not accept keyword arguments" );
100
- return NULL ;
101
- }
102
-
103
- if (PyTuple_GET_SIZE (args ) != 1 ) {
104
- PyErr_SetString (PyExc_TypeError ,
105
- "stolenbuf requires exactly 1 positional argument" );
106
- return NULL ;
107
-
108
- }
109
-
110
- /* pull out the single, positional argument */
111
- bytes_rvalue = PyTuple_GET_ITEM (args , 0 );
112
-
113
- if (!PyString_CheckExact (bytes_rvalue )) {
114
- PyErr_SetString (PyExc_TypeError ,
115
- "stolenbuf can only steal from bytes objects" );
116
- return NULL ;
117
- }
118
-
119
- if (Py_REFCNT (bytes_rvalue ) != 1 || PyString_CHECK_INTERNED (bytes_rvalue )) {
120
- /* there is a reference other than the caller's stack or the string is
121
- interned */
122
- PyErr_SetObject (badmove , bytes_rvalue );
123
- return NULL ;
124
- }
125
-
126
- if (!(ret = PyObject_New (stolenbufobject , & stolenbuf_type ))) {
127
- return NULL ;
128
- }
91
+ PyDoc_STRVAR (stolenbuf_doc ,
92
+ "A buffer that is wrapping a stolen bytes object's buffer." );
129
93
130
- /* store the original bytes object in a field that is not
131
- exposed to python */
132
- Py_INCREF (bytes_rvalue );
133
- ret -> invalid_bytes = bytes_rvalue ;
134
- return (PyObject * ) ret ;
135
- }
94
+ PyTypeObject stolenbuf_type = {
95
+ PyVarObject_HEAD_INIT (NULL , 0 )
96
+ "pandas.util._move.stolenbuf" , /* tp_name */
97
+ sizeof (stolenbufobject ), /* tp_basicsize */
98
+ 0 , /* tp_itemsize */
99
+ (destructor ) stolenbuf_dealloc , /* tp_dealloc */
100
+ 0 , /* tp_print */
101
+ 0 , /* tp_getattr */
102
+ 0 , /* tp_setattr */
103
+ 0 , /* tp_reserved */
104
+ 0 , /* tp_repr */
105
+ 0 , /* tp_as_number */
106
+ 0 , /* tp_as_sequence */
107
+ 0 , /* tp_as_mapping */
108
+ 0 , /* tp_hash */
109
+ 0 , /* tp_call */
110
+ 0 , /* tp_str */
111
+ 0 , /* tp_getattro */
112
+ 0 , /* tp_setattro */
113
+ & stolenbuf_as_buffer , /* tp_as_buffer */
114
+ Py_TPFLAGS_DEFAULT |
115
+ Py_TPFLAGS_HAVE_NEWBUFFER |
116
+ Py_TPFLAGS_HAVE_GETCHARBUFFER , /* tp_flags */
117
+ stolenbuf_doc , /* tp_doc */
118
+ };
136
119
137
120
PyDoc_STRVAR (
138
- stolenbuf_doc ,
121
+ move_into_mutable_buffer_doc ,
139
122
"Moves a bytes object that is about to be destroyed into a mutable buffer\n"
140
123
"without copying the data.\n"
141
124
"\n"
@@ -159,49 +142,55 @@ PyDoc_STRVAR(
159
142
"\n"
160
143
"Notes\n"
161
144
"-----\n"
162
- "If you want to use this function you are probably wrong.\n" );
145
+ "If you want to use this function you are probably wrong.\n"
146
+ "\n"
147
+ "Warning: Do not call this function through *unpacking. This can\n"
148
+ "potentially trick the reference checks which may allow you to get a\n"
149
+ "mutable reference to a shared string!\n"
150
+ "\n" );
151
+
152
+ /* This is implemented as a standalone function instead of the ``tp_new`` of
153
+ ``stolenbuf`` because we need to create a function using the METH_O flag
154
+ to support Python 3.6. In python 3.6, PyCFunction calls from python code now
155
+ count the reference owned by the argument tuple. This would cause the object
156
+ to have 2 references if used with a direct call like: ``stolenbuf(a)``;
157
+ however, if called through *unpacking like ``stolenbuf(*(a,))`` it would
158
+ only have the one reference (the tuple). */
159
+ static PyObject *
160
+ move_into_mutable_buffer (PyObject * self , PyObject * bytes_rvalue )
161
+ {
162
+ stolenbufobject * ret ;
163
163
164
- PyTypeObject stolenbuf_type = {
165
- PyVarObject_HEAD_INIT (NULL , 0 )
166
- "pandas.util._move.stolenbuf" , /* tp_name */
167
- sizeof (stolenbufobject ), /* tp_basicsize */
168
- 0 , /* tp_itemsize */
169
- (destructor ) stolenbuf_dealloc , /* tp_dealloc */
170
- 0 , /* tp_print */
171
- 0 , /* tp_getattr */
172
- 0 , /* tp_setattr */
173
- 0 , /* tp_reserved */
174
- 0 , /* tp_repr */
175
- 0 , /* tp_as_number */
176
- 0 , /* tp_as_sequence */
177
- 0 , /* tp_as_mapping */
178
- 0 , /* tp_hash */
179
- 0 , /* tp_call */
180
- 0 , /* tp_str */
181
- 0 , /* tp_getattro */
182
- 0 , /* tp_setattro */
183
- & stolenbuf_as_buffer , /* tp_as_buffer */
184
- Py_TPFLAGS_DEFAULT |
185
- Py_TPFLAGS_HAVE_NEWBUFFER |
186
- Py_TPFLAGS_HAVE_GETCHARBUFFER , /* tp_flags */
187
- stolenbuf_doc , /* tp_doc */
188
- 0 , /* tp_traverse */
189
- 0 , /* tp_clear */
190
- 0 , /* tp_richcompare */
191
- 0 , /* tp_weaklistoffset */
192
- 0 , /* tp_iter */
193
- 0 , /* tp_iternext */
194
- 0 , /* tp_methods */
195
- 0 , /* tp_members */
196
- 0 , /* tp_getset */
197
- 0 , /* tp_base */
198
- 0 , /* tp_dict */
199
- 0 , /* tp_descr_get */
200
- 0 , /* tp_descr_set */
201
- 0 , /* tp_dictoffset */
202
- 0 , /* tp_init */
203
- 0 , /* tp_alloc */
204
- (newfunc ) stolenbuf_new , /* tp_new */
164
+ if (!PyString_CheckExact (bytes_rvalue )) {
165
+ PyErr_SetString (PyExc_TypeError ,
166
+ "stolenbuf can only steal from bytes objects" );
167
+ return NULL ;
168
+ }
169
+
170
+ if (Py_REFCNT (bytes_rvalue ) != 1 || PyString_CHECK_INTERNED (bytes_rvalue )) {
171
+ /* there is a reference other than the caller's stack or the string is
172
+ interned */
173
+ PyErr_SetObject (badmove , bytes_rvalue );
174
+ return NULL ;
175
+ }
176
+
177
+ if (!(ret = PyObject_New (stolenbufobject , & stolenbuf_type ))) {
178
+ return NULL ;
179
+ }
180
+
181
+ /* store the original bytes object in a field that is not
182
+ exposed to python */
183
+ Py_INCREF (bytes_rvalue );
184
+ ret -> invalid_bytes = bytes_rvalue ;
185
+ return (PyObject * ) ret ;
186
+ }
187
+
188
+ PyMethodDef methods [] = {
189
+ {"move_into_mutable_buffer" ,
190
+ (PyCFunction ) move_into_mutable_buffer ,
191
+ METH_O ,
192
+ move_into_mutable_buffer_doc },
193
+ {NULL },
205
194
};
206
195
207
196
#define MODULE_NAME "pandas.util._move"
@@ -212,6 +201,7 @@ PyModuleDef _move_module = {
212
201
MODULE_NAME ,
213
202
NULL ,
214
203
-1 ,
204
+ methods ,
215
205
};
216
206
#endif /* !COMPILING_IN_PY2 */
217
207
@@ -223,7 +213,7 @@ PyDoc_STRVAR(
223
213
"Parameters\n"
224
214
"----------\n"
225
215
"data : any\n"
226
- " The data which was passed to ``_move_into_mutable_buffer ``.\n"
216
+ " The data which was passed to ``move_into_mutable_buffer ``.\n"
227
217
"\n"
228
218
"See Also\n"
229
219
"--------\n"
@@ -254,14 +244,14 @@ init_move(void)
254
244
#if !COMPILING_IN_PY2
255
245
if (!(m = PyModule_Create (& _move_module )))
256
246
#else
257
- if (!(m = Py_InitModule (MODULE_NAME , NULL )))
247
+ if (!(m = Py_InitModule (MODULE_NAME , methods )))
258
248
#endif /* !COMPILING_IN_PY2 */
259
249
{
260
250
return ERROR_RETURN ;
261
251
}
262
252
263
253
if (PyModule_AddObject (m ,
264
- "move_into_mutable_buffer " ,
254
+ "stolenbuf " ,
265
255
(PyObject * ) & stolenbuf_type )) {
266
256
Py_DECREF (m );
267
257
return ERROR_RETURN ;
0 commit comments