@@ -107,7 +107,7 @@ struct HandlerInfo {
107107static struct HandlerInfo handler_info [64 ];
108108
109109static int
110- set_error_code (PyObject * err , enum XML_Error code )
110+ set_xml_error_attr_code (PyObject * err , enum XML_Error code )
111111{
112112 PyObject * v = PyLong_FromLong ((long )code );
113113 int ok = v != NULL && PyObject_SetAttr (err , & _Py_ID (code ), v ) != -1 ;
@@ -119,7 +119,7 @@ set_error_code(PyObject *err, enum XML_Error code)
119119 * false on an exception.
120120 */
121121static int
122- set_error_location (PyObject * err , const char * name , XML_Size value )
122+ set_xml_error_attr_location (PyObject * err , const char * name , XML_Size value )
123123{
124124 PyObject * v = PyLong_FromSize_t ((size_t )value );
125125 int ok = v != NULL && PyObject_SetAttrString (err , name , v ) != -1 ;
@@ -128,42 +128,32 @@ set_error_location(PyObject *err, const char *name, XML_Size value)
128128}
129129
130130
131- static PyObject *
132- format_xml_error (enum XML_Error code , XML_Size lineno , XML_Size column )
133- {
134- const char * errmsg = XML_ErrorString (code );
135- PyUnicodeWriter * writer = PyUnicodeWriter_Create (strlen (errmsg ) + 1 );
136- if (writer == NULL ) {
137- return NULL ;
138- }
139- if (PyUnicodeWriter_Format (writer ,
140- "%s: line %zu, column %zu" ,
141- errmsg , (size_t )lineno , (size_t )column ) < 0 )
142- {
143- PyUnicodeWriter_Discard (writer );
144- return NULL ;
145- }
146- return PyUnicodeWriter_Finish (writer );
147- }
148-
149131static PyObject *
150132set_xml_error (pyexpat_state * state ,
151133 enum XML_Error code , XML_Size lineno , XML_Size column ,
152134 const char * errmsg )
153135{
154- PyObject * arg = errmsg == NULL
155- ? format_xml_error (code , lineno , column )
156- : PyUnicode_FromStringAndSize (errmsg , strlen (errmsg ));
136+ PyObject * arg ;
137+ if (errmsg == NULL ) {
138+ arg = PyUnicode_FromFormat (
139+ "%s: line %zu, column %zu" ,
140+ XML_ErrorString (code ),
141+ (size_t )lineno , (size_t )column
142+ );
143+ }
144+ else {
145+ arg = PyUnicode_FromStringAndSize (errmsg , strlen (errmsg ));
146+ }
157147 if (arg == NULL ) {
158148 return NULL ;
159149 }
160150 PyObject * res = PyObject_CallOneArg (state -> error , arg );
161151 Py_DECREF (arg );
162152 if (
163153 res != NULL
164- && set_error_code (res , code )
165- && set_error_location (res , "lineno" , lineno )
166- && set_error_location (res , "offset" , column )
154+ && set_xml_error_attr_code (res , code )
155+ && set_xml_error_attr_location (res , "lineno" , lineno )
156+ && set_xml_error_attr_location (res , "offset" , column )
167157 ) {
168158 PyErr_SetObject (state -> error , res );
169159 }
@@ -1147,6 +1137,50 @@ pyexpat_xmlparser_UseForeignDTD_impl(xmlparseobject *self, PyTypeObject *cls,
11471137}
11481138#endif
11491139
1140+ #if XML_COMBINED_VERSION >= 20702
1141+ static PyObject *
1142+ set_activation_threshold (xmlparseobject * self ,
1143+ PyTypeObject * cls ,
1144+ unsigned long long threshold ,
1145+ XML_Bool (* setter )(XML_Parser , unsigned long long ))
1146+ {
1147+ assert (self -> itself != NULL );
1148+ if (setter (self -> itself , threshold ) == XML_TRUE ) {
1149+ Py_RETURN_NONE ;
1150+ }
1151+ // The setter fails if self->itself is NULL (which is not possible here)
1152+ // or is a non-root parser, which currently only happens for parsers
1153+ // created by ExternalEntityParserCreate().
1154+ pyexpat_state * state = PyType_GetModuleState (cls );
1155+ return set_invalid_arg (state , self , "parser must be a root parser" );
1156+ }
1157+
1158+ static PyObject *
1159+ set_maximum_amplification (xmlparseobject * self ,
1160+ PyTypeObject * cls ,
1161+ float max_factor ,
1162+ XML_Bool (* setter )(XML_Parser , float ))
1163+ {
1164+ assert (self -> itself != NULL );
1165+ if (setter (self -> itself , max_factor ) == XML_TRUE ) {
1166+ Py_RETURN_NONE ;
1167+ }
1168+ // The setter fails if self->itself is NULL (which is not possible here),
1169+ // is a non-root parser, which currently only happens for parsers created
1170+ // by ExternalEntityParserCreate(), or if 'max_factor' is NaN or < 1.0.
1171+ pyexpat_state * state = PyType_GetModuleState (cls );
1172+ // Note: Expat has no API to determine whether a parser is a root parser,
1173+ // and since the Expat functions for defining the various maximum allowed
1174+ // amplifcation factors fail when a bad parser or an out-of-range factor
1175+ // is given without specifying which check failed, we check whether the
1176+ // factor is out-of-range to improve the error message. See also gh-90949.
1177+ const char * message = (isnan (max_factor ) || max_factor < 1.0f )
1178+ ? "'max_factor' must be at least 1.0"
1179+ : "parser must be a root parser" ;
1180+ return set_invalid_arg (state , self , message );
1181+ }
1182+ #endif
1183+
11501184#if XML_COMBINED_VERSION >= 20702
11511185/*[clinic input]
11521186pyexpat.xmlparser.SetAllocTrackerActivationThreshold
@@ -1166,15 +1200,10 @@ pyexpat_xmlparser_SetAllocTrackerActivationThreshold_impl(xmlparseobject *self,
11661200 unsigned long long threshold )
11671201/*[clinic end generated code: output=bed7e93207ba08c5 input=9c706b75c18e4ea1]*/
11681202{
1169- assert (self -> itself != NULL );
1170- if (XML_SetAllocTrackerActivationThreshold (self -> itself , threshold ) == XML_TRUE ) {
1171- Py_RETURN_NONE ;
1172- }
1173- // XML_SetAllocTrackerActivationThreshold() can only fail if self->itself
1174- // is not a root parser (currently, this is equivalent to be created
1175- // by ExternalEntityParserCreate()).
1176- pyexpat_state * state = PyType_GetModuleState (cls );
1177- return set_invalid_arg (state , self , "parser must be a root parser" );
1203+ return set_activation_threshold (
1204+ self , cls , threshold ,
1205+ XML_SetAllocTrackerActivationThreshold
1206+ );
11781207}
11791208#endif
11801209
@@ -1207,24 +1236,10 @@ pyexpat_xmlparser_SetAllocTrackerMaximumAmplification_impl(xmlparseobject *self,
12071236 float max_factor )
12081237/*[clinic end generated code: output=6e44bd48c9b112a0 input=918b9266b490a722]*/
12091238{
1210- assert (self -> itself != NULL );
1211- if (XML_SetAllocTrackerMaximumAmplification (self -> itself , max_factor ) == XML_TRUE ) {
1212- Py_RETURN_NONE ;
1213- }
1214- // XML_SetAllocTrackerMaximumAmplification() can fail if self->itself
1215- // is not a root parser (currently, this is equivalent to be created
1216- // by ExternalEntityParserCreate()) or if 'max_factor' is NaN or < 1.0.
1217- //
1218- // Expat does not provide a way to determine whether a parser is a root
1219- // or not, nor does it provide a way to distinguish between failures in
1220- // XML_SetAllocTrackerMaximumAmplification() (see gh-90949), we manually
1221- // detect the factor out-of-range issue here so that users have a better
1222- // error message.
1223- pyexpat_state * state = PyType_GetModuleState (cls );
1224- const char * message = (isnan (max_factor ) || max_factor < 1.0f )
1225- ? "'max_factor' must be at least 1.0"
1226- : "parser must be a root parser" ;
1227- return set_invalid_arg (state , self , message );
1239+ return set_maximum_amplification (
1240+ self , cls , max_factor ,
1241+ XML_SetAllocTrackerMaximumAmplification
1242+ );
12281243}
12291244#endif
12301245
0 commit comments