2013-01-22 6 views
6

Esiste un modo per eseguire la copertura rispetto a un eseguibile creato con pyinstaller? Ho provato a farlo funzionare come se fosse uno script python e non mi piaceva il file eseguibile come input (non mi aspettavo davvero che funzionasse) e sospetto che la risposta sia no, non esiste un modo semplice per eseguire la copertura rispetto a un eseguibile costruito .... (questo è sulle finestre exe)copertura su un file eseguibile congelato

il pacchetto di copertura sto usando è solo il normale pacchetto di copertura che si ottiene con "easy_install copertura" da nedbatchelder.com (http://nedbatchelder.com/code/coverage/)

+0

Quali strumenti di copertura del codice vengono utilizzati per script Python che non sono integrati in un file eseguibile? – selbie

+0

modificato per includere le informazioni –

+1

Che cosa si desidera testare la copertura? Non hai solo accesso agli script non congelati su cui potresti eseguire la copertura? –

risposta

6

questo isn' Una risposta completamente formulata, ma ciò che ho trovato finora.

Dalla mia comprensione di come pyinstaller funziona è che un binario è costruito da un piccolo programma C che incorpora un interprete python e bootstrap che caricano uno script. L'EXE costruito da PyInstaller include un archivio dopo la fine del binario effettivo che contiene le risorse per il codice python. Questo è spiegato qui http://www.pyinstaller.org/export/develop/project/doc/Manual.html#pyinstaller-archives.

Esiste iu.py da Pyinstaller/loader/iu.py Docs. Dovresti essere in grado di creare un hook di importazione da importare dal binario. Googling per pyinstaller disassembler ha trovato https://bitbucket.org/Trundle/exetractor/src/00df9ce00e1a/exetractor/pyinstaller.py che sembra possa estrarre le parti necessarie.

L'altra parte di questo è che tutte le risorse nell'archivio binario saranno codice python compilato. Molto probabilmente, coverage.py ti darà output inutile allo stesso modo di quando colpisci qualsiasi altro modulo compilato quando gira in condizioni normali.

3

Evidenziare uso cover_pylib=True

So che questo è molto tempo dopo aver fatto la domanda, ma sto solo andare in giro per bisogno la risposta. :)

Utilizzo della sorgente bitbucket corrente per coverage.py Sono in grado di raccogliere correttamente i dati di copertura da un file EXE generato da PyInstaller.

Nella principale fonte per la mia applicazione ho condizionatamente dico la copertura di avviare la raccolta di copertura in questo modo:

if os.environ.has_key('COVERAGE') and len(os.environ['COVERAGE']) > 0: 
    usingCoverage = True 
    import coverage 
    import time 
    cov = coverage.coverage(data_file='.coverage.' + version.GetFullString(), data_suffix=time.strftime(".%Y_%m_%d_%H_%M.%S", time.localtime()), cover_pylib=True) 
    cov.start() 

Questo avvia la raccolta di copertura SOLO quando voglio. L'uso di data_suffix mi consente di utilizzare più facilmente cov.combine() per unire il file di copertura in un secondo momento. version.GetFullString() è solo il numero di versione della mia applicazione.

cover_pylib è impostato True qui in perché tutti i moduli standard Python biblioteca __file__ attributo simile a questa ...\_MEIXXXXX\random.pyc e sono quindi indistinguibili (su base percorso) da altro codice che non esiste all'interno di un pacchetto.

Quando l'applicazione è pronta per uscire ho questo piccolo frammento:

if usingCoverage: 
    cov.stop() 
    cov.save() 

Una volta che la mia domanda è gestito coverage.py ancora non genererà automaticamente il suo rapporto HTML per me. I dati di copertura devono essere ripuliti in modo che i riferimenti del file ...\_MEIXXXX\... vengano trasformati in percorsi di file assoluti al codice sorgente reale.

faccio questo eseguendo questo frammento di codice:

import sys 
import os.path 

from coverage.data import CoverageData 
from coverage import coverage 

from glob import glob 

def cleanupLines(data): 
    """ 
    The coverage data collected via PyInstaller coverage needs the data fixed up 
    so that coverage.py's report generation code can analyze the source code. 
    PyInstaller __file__ attributes on code objecters are all in subdirectories of the  _MEIXXXX 
    temporary subdirectory. We need to replace the _MEIXXXX temp directory prefix with  the correct 
    prefix for each source file. 
    """ 
    prefix = None 
    for file, lines in data.lines.iteritems(): 
     origFile = file 
     if prefix is None: 
      index = file.find('_MEI') 
      if index >= 0: 
       pathSepIndex = file.find('\\', index) 
       if pathSepIndex >= 0: 
        prefix = file[:pathSepIndex + 1] 
     if prefix is not None and file.find(prefix) >= 0: 
      file = file.replace(prefix, "", 1) 
      for path in sys.path: 
       if os.path.exists(path) and os.path.isdir(path): 
        fileName = os.path.join(path, file) 
        if os.path.exists(fileName) and os.path.isfile(fileName): 
         file = fileName 
      if origFile != file: 
       del data.lines[origFile] 
       data.lines[file] = lines 

for file in glob('.coverage.' + version.GetFullString() + '*'): 
    print "Cleaning up: ", file 
    data = CoverageData(file) 
    data.read() 
    cleanupLines(data) 
    data.write() 

Il ciclo qui è solo per garantire tutti i file di copertura che verranno combinati vengono ripuliti.

NOTA: gli unici dati di copertura che questo codice non ripulisce per impostazione predefinita sono i file relativi a PyInstaller che non includono i dati _MEIXXX negli attributi __file__.

È ora possibile generare correttamente un codice HTML o XML (o qualsiasi altra cosa) coverage.py segnalare nel modo normale.

Nel mio caso assomiglia a questo:

cov = coverage(data_file='.coverage.' + version.GetFullString(), data_suffix='.combined') 
cov.load() 
cov.combine() 
cov.save() 
cov.load() 
cov.html_report(ignore_errors=True,omit=[r'c:\python27\*', r'..\3rdParty\PythonPackages\*']) 

L'uso del data_file nel costruttore è quello di garantire che il carico/combinare riconoscerà tutti i miei file di copertura puliti correttamente.

La chiamata html_report dice coverage.py di ignorare le librerie standard di Python (e le librerie Python controllato nella mia struttura di controllo di versione) e concentrarsi su solo il mio codice dell'applicazione.

Spero che questo aiuti.