@@ -123,7 +123,7 @@ CALL_XML_HANDLER_SETTER(const struct HandlerInfo *handler_info,
123123}
124124
125125static int
126- set_error_code (PyObject * err , enum XML_Error code )
126+ set_xml_error_attr_code (PyObject * err , enum XML_Error code )
127127{
128128 PyObject * v = PyLong_FromLong ((long )code );
129129 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)
135135 * false on an exception.
136136 */
137137static 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 )
139139{
140140 PyObject * v = PyLong_FromSize_t ((size_t )value );
141141 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)
144144}
145145
146146
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-
165147static PyObject *
166148set_xml_error (pyexpat_state * state ,
167149 enum XML_Error code , XML_Size lineno , XML_Size column ,
168150 const char * errmsg )
169151{
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+ }
173163 if (arg == NULL ) {
174164 return NULL ;
175165 }
176166 PyObject * res = PyObject_CallOneArg (state -> error , arg );
177167 Py_DECREF (arg );
178168 if (
179169 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 )
183173 ) {
184174 PyErr_SetObject (state -> error , res );
185175 }
@@ -1176,6 +1166,50 @@ pyexpat_xmlparser_UseForeignDTD_impl(xmlparseobject *self, PyTypeObject *cls,
11761166}
11771167#endif
11781168
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+
11791213#if XML_COMBINED_VERSION >= 20702
11801214/*[clinic input]
11811215pyexpat.xmlparser.SetAllocTrackerActivationThreshold
@@ -1195,15 +1229,10 @@ pyexpat_xmlparser_SetAllocTrackerActivationThreshold_impl(xmlparseobject *self,
11951229 unsigned long long threshold )
11961230/*[clinic end generated code: output=bed7e93207ba08c5 input=9c706b75c18e4ea1]*/
11971231{
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+ );
12071236}
12081237#endif
12091238
@@ -1236,24 +1265,10 @@ pyexpat_xmlparser_SetAllocTrackerMaximumAmplification_impl(xmlparseobject *self,
12361265 float max_factor )
12371266/*[clinic end generated code: output=6e44bd48c9b112a0 input=918b9266b490a722]*/
12381267{
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+ );
12571272}
12581273#endif
12591274
0 commit comments