Skip to content

Commit 73b4492

Browse files
authored
gh-119182: Use public PyUnicodeWriter API in ga_repr() (#120799)
The public PyUnicodeWriter API enables overallocation by default and so is more efficient. Benchmark: python -m pyperf timeit \ -s 't = list[int, float, complex, str, bytes, bytearray, ' \ 'memoryview, list, dict]' \ 'str(t)' Result: 1.49 us +- 0.03 us -> 1.10 us +- 0.02 us: 1.35x faster
1 parent 31ce5c0 commit 73b4492

File tree

1 file changed

+35
-33
lines changed

1 file changed

+35
-33
lines changed

Objects/genericaliasobject.c

Lines changed: 35 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -51,16 +51,15 @@ ga_traverse(PyObject *self, visitproc visit, void *arg)
5151
}
5252

5353
static int
54-
ga_repr_item(_PyUnicodeWriter *writer, PyObject *p)
54+
ga_repr_item(PyUnicodeWriter *writer, PyObject *p)
5555
{
5656
PyObject *qualname = NULL;
5757
PyObject *module = NULL;
58-
PyObject *r = NULL;
5958
int rc;
6059

6160
if (p == Py_Ellipsis) {
6261
// The Ellipsis object
63-
r = PyUnicode_FromString("...");
62+
rc = PyUnicodeWriter_WriteUTF8(writer, "...", 3);
6463
goto done;
6564
}
6665

@@ -71,17 +70,17 @@ ga_repr_item(_PyUnicodeWriter *writer, PyObject *p)
7170
goto use_repr;
7271
}
7372
if (rc < 0) {
74-
goto done;
73+
goto error;
7574
}
7675

7776
if (PyObject_GetOptionalAttr(p, &_Py_ID(__qualname__), &qualname) < 0) {
78-
goto done;
77+
goto error;
7978
}
8079
if (qualname == NULL) {
8180
goto use_repr;
8281
}
8382
if (PyObject_GetOptionalAttr(p, &_Py_ID(__module__), &module) < 0) {
84-
goto done;
83+
goto error;
8584
}
8685
if (module == NULL || module == Py_None) {
8786
goto use_repr;
@@ -92,45 +91,42 @@ ga_repr_item(_PyUnicodeWriter *writer, PyObject *p)
9291
_PyUnicode_EqualToASCIIString(module, "builtins"))
9392
{
9493
// builtins don't need a module name
95-
r = PyObject_Str(qualname);
94+
rc = PyUnicodeWriter_WriteStr(writer, qualname);
9695
goto done;
9796
}
9897
else {
99-
r = PyUnicode_FromFormat("%S.%S", module, qualname);
98+
rc = PyUnicodeWriter_Format(writer, "%S.%S", module, qualname);
10099
goto done;
101100
}
102101

102+
error:
103+
rc = -1;
104+
goto done;
105+
103106
use_repr:
104-
r = PyObject_Repr(p);
107+
rc = PyUnicodeWriter_WriteRepr(writer, p);
108+
goto done;
105109

106110
done:
107111
Py_XDECREF(qualname);
108112
Py_XDECREF(module);
109-
if (r == NULL) {
110-
// error if any of the above PyObject_Repr/PyUnicode_From* fail
111-
rc = -1;
112-
}
113-
else {
114-
rc = _PyUnicodeWriter_WriteStr(writer, r);
115-
Py_DECREF(r);
116-
}
117113
return rc;
118114
}
119115

120116
static int
121-
ga_repr_items_list(_PyUnicodeWriter *writer, PyObject *p)
117+
ga_repr_items_list(PyUnicodeWriter *writer, PyObject *p)
122118
{
123119
assert(PyList_CheckExact(p));
124120

125121
Py_ssize_t len = PyList_GET_SIZE(p);
126122

127-
if (_PyUnicodeWriter_WriteASCIIString(writer, "[", 1) < 0) {
123+
if (PyUnicodeWriter_WriteChar(writer, '[') < 0) {
128124
return -1;
129125
}
130126

131127
for (Py_ssize_t i = 0; i < len; i++) {
132128
if (i > 0) {
133-
if (_PyUnicodeWriter_WriteASCIIString(writer, ", ", 2) < 0) {
129+
if (PyUnicodeWriter_WriteUTF8(writer, ", ", 2) < 0) {
134130
return -1;
135131
}
136132
}
@@ -140,7 +136,7 @@ ga_repr_items_list(_PyUnicodeWriter *writer, PyObject *p)
140136
}
141137
}
142138

143-
if (_PyUnicodeWriter_WriteASCIIString(writer, "]", 1) < 0) {
139+
if (PyUnicodeWriter_WriteChar(writer, ']') < 0) {
144140
return -1;
145141
}
146142

@@ -153,49 +149,55 @@ ga_repr(PyObject *self)
153149
gaobject *alias = (gaobject *)self;
154150
Py_ssize_t len = PyTuple_GET_SIZE(alias->args);
155151

156-
_PyUnicodeWriter writer;
157-
_PyUnicodeWriter_Init(&writer);
152+
// Estimation based on the shortest format: "int[int, int, int]"
153+
Py_ssize_t estimate = (len <= PY_SSIZE_T_MAX / 5) ? len * 5 : len;
154+
estimate = 3 + 1 + estimate + 1;
155+
PyUnicodeWriter *writer = PyUnicodeWriter_Create(estimate);
156+
if (writer == NULL) {
157+
return NULL;
158+
}
158159

159160
if (alias->starred) {
160-
if (_PyUnicodeWriter_WriteASCIIString(&writer, "*", 1) < 0) {
161+
if (PyUnicodeWriter_WriteChar(writer, '*') < 0) {
161162
goto error;
162163
}
163164
}
164-
if (ga_repr_item(&writer, alias->origin) < 0) {
165+
if (ga_repr_item(writer, alias->origin) < 0) {
165166
goto error;
166167
}
167-
if (_PyUnicodeWriter_WriteASCIIString(&writer, "[", 1) < 0) {
168+
if (PyUnicodeWriter_WriteChar(writer, '[') < 0) {
168169
goto error;
169170
}
170171
for (Py_ssize_t i = 0; i < len; i++) {
171172
if (i > 0) {
172-
if (_PyUnicodeWriter_WriteASCIIString(&writer, ", ", 2) < 0) {
173+
if (PyUnicodeWriter_WriteUTF8(writer, ", ", 2) < 0) {
173174
goto error;
174175
}
175176
}
176177
PyObject *p = PyTuple_GET_ITEM(alias->args, i);
177178
if (PyList_CheckExact(p)) {
178179
// Looks like we are working with ParamSpec's list of type args:
179-
if (ga_repr_items_list(&writer, p) < 0) {
180+
if (ga_repr_items_list(writer, p) < 0) {
180181
goto error;
181182
}
182183
}
183-
else if (ga_repr_item(&writer, p) < 0) {
184+
else if (ga_repr_item(writer, p) < 0) {
184185
goto error;
185186
}
186187
}
187188
if (len == 0) {
188189
// for something like tuple[()] we should print a "()"
189-
if (_PyUnicodeWriter_WriteASCIIString(&writer, "()", 2) < 0) {
190+
if (PyUnicodeWriter_WriteUTF8(writer, "()", 2) < 0) {
190191
goto error;
191192
}
192193
}
193-
if (_PyUnicodeWriter_WriteASCIIString(&writer, "]", 1) < 0) {
194+
if (PyUnicodeWriter_WriteChar(writer, ']') < 0) {
194195
goto error;
195196
}
196-
return _PyUnicodeWriter_Finish(&writer);
197+
return PyUnicodeWriter_Finish(writer);
198+
197199
error:
198-
_PyUnicodeWriter_Dealloc(&writer);
200+
PyUnicodeWriter_Discard(writer);
199201
return NULL;
200202
}
201203

0 commit comments

Comments
 (0)