Skip to content

Commit e9c5318

Browse files
committed
Closes #15512: Correct __sizeof__ support for parser
1 parent a9a53c7 commit e9c5318

File tree

5 files changed

+85
-1
lines changed

5 files changed

+85
-1
lines changed

Include/node.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ PyAPI_FUNC(node *) PyNode_New(int type);
2020
PyAPI_FUNC(int) PyNode_AddChild(node *n, int type,
2121
char *str, int lineno, int col_offset);
2222
PyAPI_FUNC(void) PyNode_Free(node *n);
23+
#ifndef Py_LIMITED_API
24+
Py_ssize_t _PyNode_SizeOf(node *n);
25+
#endif
2326

2427
/* Node access functions */
2528
#define NCH(n) ((n)->n_nchildren)

Lib/test/test_parser.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import unittest
33
import sys
44
import operator
5+
import struct
56
from test import support
67

78
#
@@ -675,6 +676,46 @@ def test_comparisons(self):
675676
self.assertRaises(TypeError, operator.lt, st1, 1815)
676677
self.assertRaises(TypeError, operator.gt, b'waterloo', st2)
677678

679+
check_sizeof = support.check_sizeof
680+
681+
@support.cpython_only
682+
def test_sizeof(self):
683+
def XXXROUNDUP(n):
684+
if n <= 1:
685+
return n
686+
if n <= 128:
687+
return (n + 3) & ~3
688+
return 1 << (n - 1).bit_length()
689+
690+
basesize = support.calcobjsize('Pii')
691+
nodesize = struct.calcsize('hP3iP0h')
692+
def sizeofchildren(node):
693+
if node is None:
694+
return 0
695+
res = 0
696+
hasstr = len(node) > 1 and isinstance(node[-1], str)
697+
if hasstr:
698+
res += len(node[-1]) + 1
699+
children = node[1:-1] if hasstr else node[1:]
700+
if children:
701+
res += XXXROUNDUP(len(children)) * nodesize
702+
res1 = res
703+
if children:
704+
for child in children:
705+
res += sizeofchildren(child)
706+
return res
707+
708+
def check_st_sizeof(st):
709+
self.check_sizeof(st, basesize + nodesize +
710+
sizeofchildren(st.totuple()))
711+
712+
check_st_sizeof(parser.expr('2 + 3'))
713+
check_st_sizeof(parser.expr('2 + 3 + 4'))
714+
check_st_sizeof(parser.suite('x = 2 + 3'))
715+
check_st_sizeof(parser.suite(''))
716+
check_st_sizeof(parser.suite('# -*- coding: utf-8 -*-'))
717+
check_st_sizeof(parser.expr('[' + '2,' * 1000 + ']'))
718+
678719

679720
# XXX tests for pickling and unpickling of ST objects should go here
680721

Misc/NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,9 @@ Library
107107
- Issue #12288: Consider '0' and '0.0' as valid initialvalue
108108
for tkinter SimpleDialog.
109109

110+
- Issue #15512: Add a __sizeof__ implementation for parser.
111+
Patch by Serhiy Storchaka.
112+
110113
- Issue #15489: Add a __sizeof__ implementation for BytesIO objects.
111114
Patch by Serhiy Storchaka.
112115

Modules/parsermodule.c

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ typedef struct {
167167

168168

169169
static void parser_free(PyST_Object *st);
170+
static PyObject* parser_sizeof(PyST_Object *, void *);
170171
static PyObject* parser_richcompare(PyObject *left, PyObject *right, int op);
171172
static PyObject* parser_compilest(PyST_Object *, PyObject *, PyObject *);
172173
static PyObject* parser_isexpr(PyST_Object *, PyObject *, PyObject *);
@@ -187,7 +188,8 @@ static PyMethodDef parser_methods[] = {
187188
PyDoc_STR("Creates a list-tree representation of this ST.")},
188189
{"totuple", (PyCFunction)parser_st2tuple, PUBLIC_METHOD_TYPE,
189190
PyDoc_STR("Creates a tuple-tree representation of this ST.")},
190-
191+
{"__sizeof__", (PyCFunction)parser_sizeof, METH_NOARGS,
192+
PyDoc_STR("Returns size in memory, in bytes.")},
191193
{NULL, NULL, 0, NULL}
192194
};
193195

@@ -361,6 +363,15 @@ parser_free(PyST_Object *st)
361363
PyObject_Del(st);
362364
}
363365

366+
static PyObject *
367+
parser_sizeof(PyST_Object *st, void *unused)
368+
{
369+
Py_ssize_t res;
370+
371+
res = sizeof(PyST_Object) + _PyNode_SizeOf(st->st_node);
372+
return PyLong_FromSsize_t(res);
373+
}
374+
364375

365376
/* parser_st2tuple(PyObject* self, PyObject* args, PyObject* kw)
366377
*

Parser/node.c

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,7 @@ PyNode_AddChild(register node *n1, int type, char *str, int lineno, int col_offs
114114

115115
/* Forward */
116116
static void freechildren(node *);
117+
static Py_ssize_t sizeofchildren(node *n);
117118

118119

119120
void
@@ -125,6 +126,16 @@ PyNode_Free(node *n)
125126
}
126127
}
127128

129+
Py_ssize_t
130+
_PyNode_SizeOf(node *n)
131+
{
132+
Py_ssize_t res = 0;
133+
134+
if (n != NULL)
135+
res = sizeof(node) + sizeofchildren(n);
136+
return res;
137+
}
138+
128139
static void
129140
freechildren(node *n)
130141
{
@@ -136,3 +147,18 @@ freechildren(node *n)
136147
if (STR(n) != NULL)
137148
PyObject_FREE(STR(n));
138149
}
150+
151+
static Py_ssize_t
152+
sizeofchildren(node *n)
153+
{
154+
Py_ssize_t res = 0;
155+
int i;
156+
for (i = NCH(n); --i >= 0; )
157+
res += sizeofchildren(CHILD(n, i));
158+
if (n->n_child != NULL)
159+
/* allocated size of n->n_child array */
160+
res += XXXROUNDUP(NCH(n)) * sizeof(node);
161+
if (STR(n) != NULL)
162+
res += strlen(STR(n)) + 1;
163+
return res;
164+
}

0 commit comments

Comments
 (0)