A quick look at PLy_spi_execute_query shows that it runs each command in a subtransaction. It pretty much has to, because the coding rules for a Python method don't permit it to just longjmp out of the Python interpreter, so it has to set up a subtransaction so it can catch any error. In this example, each subtransaction will consume an XID.