gRPC Include path
오늘 회사서 씨름한 것 중 하나다.
file not found, file does not reside within, protoc is too dumb …
위와 같은 디버깅 메세지가 나오는데 아주 불친절하다. 그래서 뭐 어쩌라고 ?
$ python -m grpc_tools.protoc -I include-path --python_out=pb2.py-out --grpc_python_out=pb2_grpc.py-out proto_file
대강 위와 같이 proto를 컴파일 하는데, 우선 구조는 아래와 같다.
- project
- A
- A1
- protoA1-path
- A2
- protoA2-path
- A1
- B
- A
- A1
- protoA1.proto
- A2
- protoA2.proto
- A1
- A
- compile.py
- A
하나의 프로토에 다음과 같은 내용이 있었다. 설명하자면, protoA2.proto
에 A/A1/protoA1.proto
를 import 하였다.
project / B / A / A1 / protoA1.proto
syntax = "proto3";
package A.A1;
message test {
}
message empty {
}
project / B / A / A2 / protoA2.proto
syntax = "proto3";
import "A/A1/protoA1.proto";
service X {
rpc Y (A.A1.test) returns (A.A1.empty) {}
}
위와 같이 protoA2.proto를 작성한 뒤 compile.py 안에서 컴파일 커맨드에 -I 인자를 아래처럼 주어 실행하도록 만들었다.
$ python -m grpc_tools.protoc -I B/ --python_out=A/A2 --grpc_python_out=A/A2 B/A/A2/protoA2.proto
이러니까 웃기게도 B하위 A부터의 구조가 그대로 project / A / A2밑으로
- project
- A
- A2
- A
- A2
- protoA2_pb2.py
- protoA2_pb2_grpc.py
- A2
- A
- A2
- A
이렇게 들어간다. 그 얘기는 -I 옵션으로 준 B 디렉터리 하위 구조를 그대로 가져온다는 말이다. 그러니까 -I에 root 디렉터리 옵션을 준다고 생각하면 된다. 물론 이거는 project 바로 밑에 그대로 B의 구조를 만들어 컴파일 하고 싶은 경우다. 어찌됐건 –python_out과 –grpc_python_out에 우리는 _pb2.py**와 **_pb2_grpc.py 의 타겟 디렉토리를 쓰는게아니라 컴파일 대상 .proto파일의 상위 구조를 생각하고 써야된다는 말이다.
즉 방금 내가 테스트한 커맨드라인을 쓰자면 아래와 같다
python -m grpc_tools.protoc -I protobuf/ --python_out=. --grpc_python_out=. protobuf/test_proto/test_B/B.proto
맨 마지막 input파일의 root는 프로젝트 폴더일 것이고 그 바로 밑에 protobuf라는 폴더안에 다음과 같이
- protobuf/test_proto/test_B/B.proto
- protobuf/test_proto/test_A/A.proto
를 만들어 두었고 B가 A를 import하고 있을 때
- project/test_proto/test_B/B_pb2_*.py
를 만들고 싶다면 위 커맨드 대로 컴파일 하면 되는 것이다.
그런데 이건 안된다.
(grpc) $ python -m grpc_tools.protoc -I protobuf/test_proto/ --python_out=test_proto/ --grpc_python_out=test_proto/ protobuf/test_proto/test_B/B.proto
test_proto/test_A/A.proto: File not found.
test_B/B.proto: Import "test_proto/test_A/A.proto" was not found or had errors.
test_B/B.proto:6:12: "test_proto.test_A.test" is not defined.
test_B/B.proto:6:45: "test_proto.test_A.empty" is not defined.
보면 B.proto 에서 우리는 A/A1/A1.proto를 import한다고 해놓고 컴파일러에 import path를 B/A 를 줬다.
A가 겹치는 셈이고 이건 파일을 B / A / A / A1 / A1.proto에서 찾겠다는 말이니 당연히 안된다. 그러니 import를 다음과 같이 바꿔줘야된다. 그래야 import에쓴 A1/protoA1.proto를 정상적으로 찾는다.
syntax = "proto3";
import "A1/protoA1.proto";
service X {
rpc Y (A.A1.test) returns (A.A1.empty) {}
}
**정리하자면, import 에는 relative path를 쓴다. 그럼 이 relative path에 바로 상위 부터를 root path라하면 이 root path를 protoc의 -I 옵션의 인자로 넣어줘야된다. **
그러니 어느 정도 구조를 맞춰놓고 import문을 다음과 같이쓰고, input으로 project하위 full path를 줘버리고 project폴더를 루트로 하여 –python_out과 –grpc_python_out에 . 을 줘버리는게 아주 마음이 편하다.
syntax = "proto3";
import "A/A1/protoA1.proto";
service X {
rpc Y (A.A1.test) returns (A.A1.empty) {}
}
python -m grpc_tools.protoc -I protobuf/ --python_out=. --grpc_python_out=. protobuf/test_proto/test_B/B.proto