@@ -111,7 +111,7 @@ struct HandlerInfo {
111111static struct HandlerInfo handler_info [64 ];
112112
113113static int
114- set_error_code (PyObject * err , enum XML_Error code )
114+ set_xml_error_attr_code (PyObject * err , enum XML_Error code )
115115{
116116 PyObject * v = PyLong_FromLong ((long )code );
117117 int ok = v != NULL && PyObject_SetAttr (err , & _Py_ID (code ), v ) != -1 ;
@@ -123,7 +123,7 @@ set_error_code(PyObject *err, enum XML_Error code)
123123 * false on an exception.
124124 */
125125static int
126- set_error_location (PyObject * err , const char * name , XML_Size value )
126+ set_xml_error_attr_location (PyObject * err , const char * name , XML_Size value )
127127{
128128 PyObject * v = PyLong_FromSize_t ((size_t )value );
129129 int ok = v != NULL && PyObject_SetAttrString (err , name , v ) != -1 ;
@@ -132,32 +132,32 @@ set_error_location(PyObject *err, const char *name, XML_Size value)
132132}
133133
134134
135- static PyObject *
136- format_xml_error (enum XML_Error code , XML_Size lineno , XML_Size column )
137- {
138- const char * errmsg = XML_ErrorString (code );
139- return PyUnicode_FromFormat ("%s: line %zu, column %zu" ,
140- errmsg , (size_t )lineno , (size_t )column );
141- }
142-
143135static PyObject *
144136set_xml_error (pyexpat_state * state ,
145137 enum XML_Error code , XML_Size lineno , XML_Size column ,
146138 const char * errmsg )
147139{
148- PyObject * arg = errmsg == NULL
149- ? format_xml_error (code , lineno , column )
150- : PyUnicode_FromStringAndSize (errmsg , strlen (errmsg ));
140+ PyObject * arg ;
141+ if (errmsg == NULL ) {
142+ arg = PyUnicode_FromFormat (
143+ "%s: line %zu, column %zu" ,
144+ XML_ErrorString (code ),
145+ (size_t )lineno , (size_t )column
146+ );
147+ }
148+ else {
149+ arg = PyUnicode_FromStringAndSize (errmsg , strlen (errmsg ));
150+ }
151151 if (arg == NULL ) {
152152 return NULL ;
153153 }
154154 PyObject * res = PyObject_CallOneArg (state -> error , arg );
155155 Py_DECREF (arg );
156156 if (
157157 res != NULL
158- && set_error_code (res , code )
159- && set_error_location (res , "lineno" , lineno )
160- && set_error_location (res , "offset" , column )
158+ && set_xml_error_attr_code (res , code )
159+ && set_xml_error_attr_location (res , "lineno" , lineno )
160+ && set_xml_error_attr_location (res , "offset" , column )
161161 ) {
162162 PyErr_SetObject (state -> error , res );
163163 }
@@ -1134,6 +1134,50 @@ pyexpat_xmlparser_UseForeignDTD_impl(xmlparseobject *self, PyTypeObject *cls,
11341134}
11351135#endif
11361136
1137+ #if XML_COMBINED_VERSION >= 20702
1138+ static PyObject *
1139+ set_activation_threshold (xmlparseobject * self ,
1140+ PyTypeObject * cls ,
1141+ unsigned long long threshold ,
1142+ XML_Bool (* setter )(XML_Parser , unsigned long long ))
1143+ {
1144+ assert (self -> itself != NULL );
1145+ if (setter (self -> itself , threshold ) == XML_TRUE ) {
1146+ Py_RETURN_NONE ;
1147+ }
1148+ // The setter fails if self->itself is NULL (which is not possible here)
1149+ // or is a non-root parser, which currently only happens for parsers
1150+ // created by ExternalEntityParserCreate().
1151+ pyexpat_state * state = PyType_GetModuleState (cls );
1152+ return set_invalid_arg (state , self , "parser must be a root parser" );
1153+ }
1154+
1155+ static PyObject *
1156+ set_maximum_amplification (xmlparseobject * self ,
1157+ PyTypeObject * cls ,
1158+ float max_factor ,
1159+ XML_Bool (* setter )(XML_Parser , float ))
1160+ {
1161+ assert (self -> itself != NULL );
1162+ if (setter (self -> itself , max_factor ) == XML_TRUE ) {
1163+ Py_RETURN_NONE ;
1164+ }
1165+ // The setter fails if self->itself is NULL (which is not possible here),
1166+ // is a non-root parser, which currently only happens for parsers created
1167+ // by ExternalEntityParserCreate(), or if 'max_factor' is NaN or < 1.0.
1168+ pyexpat_state * state = PyType_GetModuleState (cls );
1169+ // Note: Expat has no API to determine whether a parser is a root parser,
1170+ // and since the Expat functions for defining the various maximum allowed
1171+ // amplifcation factors fail when a bad parser or an out-of-range factor
1172+ // is given without specifying which check failed, we check whether the
1173+ // factor is out-of-range to improve the error message. See also gh-90949.
1174+ const char * message = (isnan (max_factor ) || max_factor < 1.0f )
1175+ ? "'max_factor' must be at least 1.0"
1176+ : "parser must be a root parser" ;
1177+ return set_invalid_arg (state , self , message );
1178+ }
1179+ #endif
1180+
11371181#if XML_COMBINED_VERSION >= 20702
11381182/*[clinic input]
11391183pyexpat.xmlparser.SetAllocTrackerActivationThreshold
@@ -1153,15 +1197,10 @@ pyexpat_xmlparser_SetAllocTrackerActivationThreshold_impl(xmlparseobject *self,
11531197 unsigned long long threshold )
11541198/*[clinic end generated code: output=bed7e93207ba08c5 input=9c706b75c18e4ea1]*/
11551199{
1156- assert (self -> itself != NULL );
1157- if (XML_SetAllocTrackerActivationThreshold (self -> itself , threshold ) == XML_TRUE ) {
1158- Py_RETURN_NONE ;
1159- }
1160- // XML_SetAllocTrackerActivationThreshold() can only fail if self->itself
1161- // is not a root parser (currently, this is equivalent to be created
1162- // by ExternalEntityParserCreate()).
1163- pyexpat_state * state = PyType_GetModuleState (cls );
1164- return set_invalid_arg (state , self , "parser must be a root parser" );
1200+ return set_activation_threshold (
1201+ self , cls , threshold ,
1202+ XML_SetAllocTrackerActivationThreshold
1203+ );
11651204}
11661205#endif
11671206
@@ -1194,24 +1233,10 @@ pyexpat_xmlparser_SetAllocTrackerMaximumAmplification_impl(xmlparseobject *self,
11941233 float max_factor )
11951234/*[clinic end generated code: output=6e44bd48c9b112a0 input=918b9266b490a722]*/
11961235{
1197- assert (self -> itself != NULL );
1198- if (XML_SetAllocTrackerMaximumAmplification (self -> itself , max_factor ) == XML_TRUE ) {
1199- Py_RETURN_NONE ;
1200- }
1201- // XML_SetAllocTrackerMaximumAmplification() can fail if self->itself
1202- // is not a root parser (currently, this is equivalent to be created
1203- // by ExternalEntityParserCreate()) or if 'max_factor' is NaN or < 1.0.
1204- //
1205- // Expat does not provide a way to determine whether a parser is a root
1206- // or not, nor does it provide a way to distinguish between failures in
1207- // XML_SetAllocTrackerMaximumAmplification() (see gh-90949), we manually
1208- // detect the factor out-of-range issue here so that users have a better
1209- // error message.
1210- pyexpat_state * state = PyType_GetModuleState (cls );
1211- const char * message = (isnan (max_factor ) || max_factor < 1.0f )
1212- ? "'max_factor' must be at least 1.0"
1213- : "parser must be a root parser" ;
1214- return set_invalid_arg (state , self , message );
1236+ return set_maximum_amplification (
1237+ self , cls , max_factor ,
1238+ XML_SetAllocTrackerMaximumAmplification
1239+ );
12151240}
12161241#endif
12171242
0 commit comments