@@ -123,7 +123,7 @@ CALL_XML_HANDLER_SETTER(const struct HandlerInfo *handler_info,
123
123
}
124
124
125
125
static int
126
- set_error_code (PyObject * err , enum XML_Error code )
126
+ set_xml_error_attr_code (PyObject * err , enum XML_Error code )
127
127
{
128
128
PyObject * v = PyLong_FromLong ((long )code );
129
129
int ok = v != NULL && PyObject_SetAttr (err , & _Py_ID (code ), v ) != -1 ;
@@ -135,7 +135,7 @@ set_error_code(PyObject *err, enum XML_Error code)
135
135
* false on an exception.
136
136
*/
137
137
static int
138
- set_error_location (PyObject * err , const char * name , XML_Size value )
138
+ set_xml_error_attr_location (PyObject * err , const char * name , XML_Size value )
139
139
{
140
140
PyObject * v = PyLong_FromSize_t ((size_t )value );
141
141
int ok = v != NULL && PyObject_SetAttrString (err , name , v ) != -1 ;
@@ -144,42 +144,32 @@ set_error_location(PyObject *err, const char *name, XML_Size value)
144
144
}
145
145
146
146
147
- static PyObject *
148
- format_xml_error (enum XML_Error code , XML_Size lineno , XML_Size column )
149
- {
150
- const char * errmsg = XML_ErrorString (code );
151
- PyUnicodeWriter * writer = PyUnicodeWriter_Create (strlen (errmsg ) + 1 );
152
- if (writer == NULL ) {
153
- return NULL ;
154
- }
155
- if (PyUnicodeWriter_Format (writer ,
156
- "%s: line %zu, column %zu" ,
157
- errmsg , (size_t )lineno , (size_t )column ) < 0 )
158
- {
159
- PyUnicodeWriter_Discard (writer );
160
- return NULL ;
161
- }
162
- return PyUnicodeWriter_Finish (writer );
163
- }
164
-
165
147
static PyObject *
166
148
set_xml_error (pyexpat_state * state ,
167
149
enum XML_Error code , XML_Size lineno , XML_Size column ,
168
150
const char * errmsg )
169
151
{
170
- PyObject * arg = errmsg == NULL
171
- ? format_xml_error (code , lineno , column )
172
- : PyUnicode_FromStringAndSize (errmsg , strlen (errmsg ));
152
+ PyObject * arg ;
153
+ if (errmsg == NULL ) {
154
+ arg = PyUnicode_FromFormat (
155
+ "%s: line %zu, column %zu" ,
156
+ XML_ErrorString (code ),
157
+ (size_t )lineno , (size_t )column
158
+ );
159
+ }
160
+ else {
161
+ arg = PyUnicode_FromStringAndSize (errmsg , strlen (errmsg ));
162
+ }
173
163
if (arg == NULL ) {
174
164
return NULL ;
175
165
}
176
166
PyObject * res = PyObject_CallOneArg (state -> error , arg );
177
167
Py_DECREF (arg );
178
168
if (
179
169
res != NULL
180
- && set_error_code (res , code )
181
- && set_error_location (res , "lineno" , lineno )
182
- && set_error_location (res , "offset" , column )
170
+ && set_xml_error_attr_code (res , code )
171
+ && set_xml_error_attr_location (res , "lineno" , lineno )
172
+ && set_xml_error_attr_location (res , "offset" , column )
183
173
) {
184
174
PyErr_SetObject (state -> error , res );
185
175
}
@@ -1176,6 +1166,50 @@ pyexpat_xmlparser_UseForeignDTD_impl(xmlparseobject *self, PyTypeObject *cls,
1176
1166
}
1177
1167
#endif
1178
1168
1169
+ #if XML_COMBINED_VERSION >= 20702
1170
+ static PyObject *
1171
+ set_activation_threshold (xmlparseobject * self ,
1172
+ PyTypeObject * cls ,
1173
+ unsigned long long threshold ,
1174
+ XML_Bool (* setter )(XML_Parser , unsigned long long ))
1175
+ {
1176
+ assert (self -> itself != NULL );
1177
+ if (setter (self -> itself , threshold ) == XML_TRUE ) {
1178
+ Py_RETURN_NONE ;
1179
+ }
1180
+ // The setter fails if self->itself is NULL (which is not possible here)
1181
+ // or is a non-root parser, which currently only happens for parsers
1182
+ // created by ExternalEntityParserCreate().
1183
+ pyexpat_state * state = PyType_GetModuleState (cls );
1184
+ return set_invalid_arg (state , self , "parser must be a root parser" );
1185
+ }
1186
+
1187
+ static PyObject *
1188
+ set_maximum_amplification (xmlparseobject * self ,
1189
+ PyTypeObject * cls ,
1190
+ float max_factor ,
1191
+ XML_Bool (* setter )(XML_Parser , float ))
1192
+ {
1193
+ assert (self -> itself != NULL );
1194
+ if (setter (self -> itself , max_factor ) == XML_TRUE ) {
1195
+ Py_RETURN_NONE ;
1196
+ }
1197
+ // The setter fails if self->itself is NULL (which is not possible here),
1198
+ // is a non-root parser, which currently only happens for parsers created
1199
+ // by ExternalEntityParserCreate(), or if 'max_factor' is NaN or < 1.0.
1200
+ pyexpat_state * state = PyType_GetModuleState (cls );
1201
+ // Note: Expat has no API to determine whether a parser is a root parser,
1202
+ // and since the Expat functions for defining the various maximum allowed
1203
+ // amplifcation factors fail when a bad parser or an out-of-range factor
1204
+ // is given without specifying which check failed, we check whether the
1205
+ // factor is out-of-range to improve the error message. See also gh-90949.
1206
+ const char * message = (isnan (max_factor ) || max_factor < 1.0f )
1207
+ ? "'max_factor' must be at least 1.0"
1208
+ : "parser must be a root parser" ;
1209
+ return set_invalid_arg (state , self , message );
1210
+ }
1211
+ #endif
1212
+
1179
1213
#if XML_COMBINED_VERSION >= 20702
1180
1214
/*[clinic input]
1181
1215
pyexpat.xmlparser.SetAllocTrackerActivationThreshold
@@ -1195,15 +1229,10 @@ pyexpat_xmlparser_SetAllocTrackerActivationThreshold_impl(xmlparseobject *self,
1195
1229
unsigned long long threshold )
1196
1230
/*[clinic end generated code: output=bed7e93207ba08c5 input=9c706b75c18e4ea1]*/
1197
1231
{
1198
- assert (self -> itself != NULL );
1199
- if (XML_SetAllocTrackerActivationThreshold (self -> itself , threshold ) == XML_TRUE ) {
1200
- Py_RETURN_NONE ;
1201
- }
1202
- // XML_SetAllocTrackerActivationThreshold() can only fail if self->itself
1203
- // is not a root parser (currently, this is equivalent to be created
1204
- // by ExternalEntityParserCreate()).
1205
- pyexpat_state * state = PyType_GetModuleState (cls );
1206
- return set_invalid_arg (state , self , "parser must be a root parser" );
1232
+ return set_activation_threshold (
1233
+ self , cls , threshold ,
1234
+ XML_SetAllocTrackerActivationThreshold
1235
+ );
1207
1236
}
1208
1237
#endif
1209
1238
@@ -1236,24 +1265,10 @@ pyexpat_xmlparser_SetAllocTrackerMaximumAmplification_impl(xmlparseobject *self,
1236
1265
float max_factor )
1237
1266
/*[clinic end generated code: output=6e44bd48c9b112a0 input=918b9266b490a722]*/
1238
1267
{
1239
- assert (self -> itself != NULL );
1240
- if (XML_SetAllocTrackerMaximumAmplification (self -> itself , max_factor ) == XML_TRUE ) {
1241
- Py_RETURN_NONE ;
1242
- }
1243
- // XML_SetAllocTrackerMaximumAmplification() can fail if self->itself
1244
- // is not a root parser (currently, this is equivalent to be created
1245
- // by ExternalEntityParserCreate()) or if 'max_factor' is NaN or < 1.0.
1246
- //
1247
- // Expat does not provide a way to determine whether a parser is a root
1248
- // or not, nor does it provide a way to distinguish between failures in
1249
- // XML_SetAllocTrackerMaximumAmplification() (see gh-90949), we manually
1250
- // detect the factor out-of-range issue here so that users have a better
1251
- // error message.
1252
- pyexpat_state * state = PyType_GetModuleState (cls );
1253
- const char * message = (isnan (max_factor ) || max_factor < 1.0f )
1254
- ? "'max_factor' must be at least 1.0"
1255
- : "parser must be a root parser" ;
1256
- return set_invalid_arg (state , self , message );
1268
+ return set_maximum_amplification (
1269
+ self , cls , max_factor ,
1270
+ XML_SetAllocTrackerMaximumAmplification
1271
+ );
1257
1272
}
1258
1273
#endif
1259
1274
0 commit comments