6

Ho un progetto Python che utilizza setuptools per la distribuzione e ho seguito principalmente this guide per quanto riguarda la struttura del progetto. Il progetto utilizza i buffer del protocollo Google per definire un formato di messaggio di rete. Il mio problema principale è come rendere setup.py chiamare il protoc-compiler durante l'installazione per creare le definizioni in un file _pb2.py.Progetto Python utilizzando buffer di protocollo, Problemi di distribuzione

In this question è stato consigliato di distribuire solo i file _pb2.py risultanti insieme al progetto. Mentre questo potrebbe funzionare per piattaforme molto simili, ho trovato diversi casi in cui questo non funziona. Ad esempio, quando sviluppo su un Mac che utilizza Anaconda Python e copio il risultante _pb2.py insieme al resto del progetto in un Raspberry Pi su cui è in esecuzione Raspbian, ci sono sempre errori di importazione provenienti dai moduli _pb2.py. Tuttavia, se compilo i file .proto di recente sul Pi, il progetto funziona come previsto. Quindi, la distribuzione dei file compilati non sembra un'opzione.

Tipo di ricerca di soluzioni di lavoro e di buone pratiche qui. Si può presumere che il protoc-compilatore sia installato sulla piattaforma di destinazione.

Edit:

Dal momento che le persone chiedono le ragioni del fallimento. Su Mac, la versione di protobuf è 2.6.1. e sul Pi è il 2.4.1. Apparentemente, l'API interna utilizzata dall'output del compilatore di protoc generato è cambiata. L'output è fondamentalmente:

File "[...]network_manager.py", line 8, in <module> 
     import InstrumentControl.transports.serial_bridge_protocol_pb2 as protocol 
    File "[...]serial_bridge_protocol_pb2.py", line 9, in <module> 
     from google.protobuf import symbol_database as _symbol_database 
    ImportError: cannot import name symbol_database 
+0

ho avuto problemi simili, e sempre compilata sulla PI. Il problema deriva da AFAIR da diverse versioni di protobuf. Quindi - quali versioni hai sulle rispettive macchine e non puoi aggiornarle allo stesso modo? – deets

+0

Mi piacerebbe davvero evitarlo. Posso (probabilmente) farlo sulle macchine che controllo ma non mi sembra il modo giusto per farlo. E probabilmente fallisce quando si distribuisce il software ad altri. Facendoli installare una versione specifica dei buffer di protocollo quando la loro versione corrente potrebbe (ri-compilando) essere usata, non sembra giusta. – jan

+0

Hai cercato - cosa causa errori di importazione? Vorrei scegliere la strategia di packaging _pb2.py files e ricercare i problemi di errore di importazione. –

risposta

9

Ok, ho risolto il problema senza richiedere all'utente di installare una specifica versione vecchia o compilare i file proto su un'altra piattaforma della mia macchina dev. È ispirato a this setup.py script from protobuf itself.

In primo luogo, ProtoC deve essere trovata, questo può essere fatto utilizzando

# Find the Protocol Compiler. 
if 'PROTOC' in os.environ and os.path.exists(os.environ['PROTOC']): 
    protoc = os.environ['PROTOC'] 
else: 
    protoc = find_executable("protoc") 

Questa funzione compilare un file Proto e mettere il _pb2.py nello stesso punto. Tuttavia, il comportamento può essere modificato arbitrariamente.

def generate_proto(source): 
    """Invokes the Protocol Compiler to generate a _pb2.py from the given 
    .proto file. Does nothing if the output already exists and is newer than 
    the input.""" 

    output = source.replace(".proto", "_pb2.py") 

    if (not os.path.exists(output) or 
     (os.path.exists(source) and 
     os.path.getmtime(source) > os.path.getmtime(output))): 
    print "Generating %s..." % output 

    if not os.path.exists(source): 
     sys.stderr.write("Can't find required file: %s\n" % source) 
     sys.exit(-1) 

    if protoc == None: 
     sys.stderr.write(
      "Protocol buffers compiler 'protoc' not installed or not found.\n" 
     ) 
     sys.exit(-1) 

    protoc_command = [ protoc, "-I.", "--python_out=.", source ] 
    if subprocess.call(protoc_command) != 0: 
     sys.exit(-1) 

Successivamente, le classi _build_py e _clean vengono derivate per aggiungere la creazione e la pulizia dei buffer del protocollo.

# List of all .proto files 
proto_src = ['file1.proto', 'path/to/file2.proto'] 

class build_py(_build_py): 
    def run(self): 
    for f in proto_src: 
     generate_proto(f) 
    _build_py.run(self) 

class clean(_clean): 
    def run(self): 
    # Delete generated files in the code tree. 
    for (dirpath, dirnames, filenames) in os.walk("."): 
     for filename in filenames: 
     filepath = os.path.join(dirpath, filename) 
     if filepath.endswith("_pb2.py"): 
      os.remove(filepath) 
    # _clean is an old-style class, so super() doesn't work. 
    _clean.run(self) 

E, infine, il parametro

cmdclass = { 'clean': clean, 'build_py': build_py } 

deve essere aggiunto alla chiamata alla messa a punto e tutto dovrebbe funzionare. Devo ancora verificare eventuali stranezze, ma finora funziona perfettamente su Mac e sul Pi.

3

Un'altra soluzione consiste nel raggruppare la libreria protobuf con l'app, anziché utilizzare la versione installata sul computer di destinazione. In questo modo sai che non c'è nessuna corrispondenza tra versione e codice generato.

3

Ho appena iniziato il pacchetto protobuf-setuptools per utilizzare la parte più sana di questo codice. Ha ancora bisogno di miglioramenti, quindi qualsiasi feedback è benvenuto!

Check it out: https://pypi.python.org/pypi/protobuf-setuptools

+0

Bello! Spero di poterlo dare un'occhiata e integrarlo presto con le nostre fonti. – jan

+0

@jan sarà felice di aiutare e migliorare. – pupssman

Problemi correlati