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