Num dos testes antigos do PySide, havia uma inocente linha de código com QFile().metaObject().methodCount(), que na nova versão estava causando uma falha de segmentação dentro da Qt. O que estava acontecendo era que o QMetaObject retornado pelo metaObject() estava sendo apagado pelo QFile() criado, invalidando a área de memória que methodCount() tentava acessar. Agora por que diabos ele estava sendo deletado, já que eu chamava o método direto nele? A resposta está no modo como o CPython é implementado, sendo uma máquina virtual de pilha.
Usando o módulo dis nessa linha, temos o seguinte resultado:
0 LOAD_GLOBAL 0 (QFile)
3 CALL_FUNCTION 0
6 LOAD_ATTR 1 (metaObject)
9 CALL_FUNCTION 0
12 LOAD_ATTR 2 (foo)
15 CALL_FUNCTION 0
18 POP_TOP
19 LOAD_CONST 0 (None)
22 RETURN_VALUE
Dissecando instrução por instrução e seus efeitos na pilha, vamos assumir que esteja inicialmente vazia. Apenas as 4 primeiras instrução são necessárias:
- LOAD_GLOBAL (QFile) – Topo da pilha é a classe QFile
- CALL_FUNCTION – Remove QFile do topo e coloca o resultado da chamada, no caso, a nova instância de QFile, com refcount 1
- LOAD_ATTR(metaObject) – Remove a instância de QFile do topo (decrementa o refcount) e coloca o resultado de getattr(instância, ‘metaObject’) no topo. Nesse caso, o resultado é um “bound method” A chamada a getattr incrementa a referência da instância de QFile, logo ela não morre.
- CALL_FUNCTION – Remove o metodo metaObject do topo e coloca o resultado, no caso a instância de QMetaObject retornada. Ao remover o método, a referência à instância de QFile é removida, chegando a 0. Então o destrutor do binding chama o destrutor de C++, que por sua vez deleta o objeto C++ do QMetaObject, invalidando o ponteiro usado pelo binding.
Ou seja, devido essas instruções, não se pode garantir que um objeto criado anonimamente numa chamada de metodo e usado imediatamente irá estar “vivo” em chamadas subsequentes.
Vale notar que esse problema aparece em outras implementações de Python baseadas no CPython, como o Stackless e o Unladen Swallow. Implementações que usam outros tipos de máquina virtual como o Jython, IronPython e Pypy não sofrem desse problema.

