6

배포를 위해 setuptools를 사용하는 Python 프로젝트가 있는데 주로 프로젝트 구조와 관련하여 this guide을 따랐습니다. 이 프로젝트는 Google 프로토콜 버퍼를 사용하여 네트워크 메시지 형식을 정의합니다. 필자의 주된 문제는 설치 중에 _pb2.py 파일에 정의를 빌드하기 위해 setup.py가 protoc 컴파일러를 호출하는 방법입니다.프로토콜 버퍼를 사용하는 Python 프로젝트, 배포 문제

this question에서 결과 _pb2.py 파일을 프로젝트와 함께 배포하도록 조언했습니다. 이것은 매우 유사한 플랫폼에서도 작동 할 수 있지만 이것이 작동하지 않는 몇 가지 경우를 발견했습니다. 예를 들어, Anaconda Python을 사용하고 나머지 프로젝트와 함께 _pb2.py를 Raspbian을 실행하는 Raspberry Pi로 복사하는 Mac에서 개발할 때 항상 _pb2.py 모듈에서 가져 오는 가져 오기 오류가 있습니다. 그러나 Pi에서 .proto 파일을 새로 컴파일하면 프로젝트가 예상대로 작동합니다. 컴파일 된 파일을 배포하는 것은 옵션처럼 보이지 않습니다.

여기서 일하는 방법과 모범 사례 솔루션을 찾는 유형. protoc 컴파일러가 대상 플랫폼에 설치되어 있다고 가정 할 수 있습니다.

편집 :

사람들은 오류의 이유를 묻습니다. Mac의 경우 protobuf 버전은 2.6.1입니다. Pi에 2.4.1입니다. 분명히 생성 된 protoc 컴파일러 출력에서 ​​사용 된 내부 API가 변경되었습니다. 출력은 기본적으로 :

확인
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

나는 비슷한 문제가 있었으며 항상 PI에서 컴파일되었습니다. 이 문제는 AFAIR이 다른 protobuf 버전에서 유래합니다. 그렇다면 각 컴퓨터에서 어떤 버전을 가지고 있으며 업그레이드 할 수 없습니까? – deets

+0

나는 그것을 피하고 싶습니다. 나는 (아마도) 내가 제어하는 ​​기계에서 그렇게 할 수는 있지만 올바른 방법은 아닐 것입니다. 그리고 다른 사람들에게 소프트웨어를 배포 할 때 아마도 실패 할 것입니다. 현재 버전을 (다시 컴파일하여) 사용할 수있을 때 특정 버전의 프로토콜 버퍼를 설치하게하는 것은 옳은 생각이 아닙니다. – jan

+0

조사 했습니까? 가져 오기 오류의 원인은 무엇입니까? 포장 _pb2.py 파일 전략을 선택하고 가져 오기 오류 문제를 조사합니다. –

답변

9

, 나는 내 dev에 기계 아닌 다른 플랫폼에서 프로토 파일을 특정 이전 버전을 설치하거나 컴파일하는 사용자를 필요로하지 않고 문제를 해결했다. this setup.py script from protobuf itself에서 영감을 얻었습니다.

는 첫째, protoc가 발견 될 필요가,이는 .proto 파일을 컴파일하고 같은 자리에 _pb2.py을 넣어 것입니다

# 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") 

이 기능을 사용하여 수행 할 수 있습니다. 그러나 동작은 임의로 변경할 수 있습니다.

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) 

다음으로 _build_py 및 _clean 클래스는 빌드를 추가하고 프로토콜 버퍼를 정리하기 위해 파생됩니다.

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

# 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) 

그리고 마지막으로, 매개 변수는 작업을해야 설정 및 모든 호출에 추가해야합니다. 여전히 가능한 단점을 확인해야하지만, 지금까지 Mac 및 Pi에서 완벽하게 작동합니다.

3

다른 해결책은 대상 컴퓨터에서 설치된 버전을 사용하는 대신 protobuf 라이브러리를 앱과 함께 제공하는 것입니다. 이렇게하면 생성 된 코드와 버전이 일치하지 않는다는 것을 알 수 있습니다.

3

나는이 코드의 가장 정상적인 부분을 사용하기 위해서 protobuf-setuptools 패키지를 방금 시작했습니다. 아직 개선이 필요하므로 모든 의견을 환영합니다!

체크 아웃 : https://pypi.python.org/pypi/protobuf-setuptools

+0

니스! 나는 그것을 살펴보고 곧 소스와 통합 할 수 있기를 바랍니다. – jan

+0

@jan은 도움을주고 기꺼이 개선 할 것입니다. – pupssman