protoc-gen-openapiv2 Python Wrapper
This Python package provides a convenient wrapper to install the `protoc-gen-openapiv2` binary. The binary generates OpenAPI v2 (Swagger) definitions directly from Protocol Buffer service definitions, making it a critical component for gRPC Gateway projects to expose gRPC services as RESTful APIs. It is currently at version 0.0.1, with development closely tied to the upstream Go project.
Warnings
- gotcha The `protoc-gen-openapiv2` Python package primarily installs a command-line binary. Its core functionality is invoked via the `protoc` command, not by importing Python modules and calling functions directly.
- gotcha The `protoc` (Protocol Buffer compiler) binary must be installed on your system and accessible in your system's PATH for this plugin to function. This Python package only installs the OpenAPI v2 generator plugin, not `protoc` itself.
- gotcha After installing `protoc-gen-openapiv2` via `pip`, the `protoc-gen-openapiv2` binary might be installed into a user-specific directory (e.g., `~/.local/bin` in Linux or `AppData\Roaming\Python\Scripts` in Windows for Python >= 3.3). This directory might not be in your system's PATH by default.
- gotcha When using `protoc`, correctly specifying include paths (`-I` flags) for all `.proto` files, especially imported ones like `google/api/annotations.proto` (common for gRPC Gateway), is critical. Incorrect paths will lead to `File not found` errors during generation.
- breaking Given its early version `0.0.1`, future updates (even minor releases) are likely to introduce breaking changes to command-line options, generated output, or dependencies without strictly adhering to semantic versioning guidelines.
Install
-
pip install protoc-gen-openapiv2
Quickstart
import subprocess
import os
import sys
# --- Prerequisites Check ---
# This library's core function is a 'protoc' plugin, so 'protoc' and the plugin
# binary must be available in your system's PATH.
def check_command(cmd_name):
return subprocess.run(["which", cmd_name], capture_output=True).returncode == 0
if not check_command("protoc"):
print("Error: 'protoc' (Protocol Buffer compiler) not found in PATH.", file=sys.stderr)
print("Please install protoc. E.g., on Debian/Ubuntu: 'sudo apt install protobuf-compiler'", file=sys.stderr)
sys.exit(1)
if not check_command("protoc-gen-openapiv2"):
print("Error: 'protoc-gen-openapiv2' not found in PATH.", file=sys.stderr)
print("This Python package installs the binary, but you may need to add its install location (e.g., ~/.local/bin) to your PATH.", file=sys.stderr)
sys.exit(1)
# --- 1. Create a dummy .proto file for demonstration ---
proto_file = "example.proto"
output_dir = "./openapi_output"
swagger_output_file = os.path.join(output_dir, "example.swagger.json")
proto_content = """
syntax = "proto3";
package example;
import "google/api/annotations.proto"; // Essential for gRPC Gateway HTTP annotations
service MyService {
rpc MyMethod (MyRequest) returns (MyResponse) {
option (google.api.http) = {
get: "/v1/example/{query}"
};
}
}
message MyRequest {
string query = 1;
}
message MyResponse {
string result = 1;
}
"""
with open(proto_file, "w") as f:
f.write(proto_content)
os.makedirs(output_dir, exist_ok=True)
# --- 2. Run protoc with the openapiv2 plugin ---
# The 'google/api/annotations.proto' needs to be discoverable by protoc.
# It's typically found in your protoc installation or the grpc-gateway repo.
# If not, you might need to add: -I/path/to/grpc-gateway/third_party/googleapis
protoc_command = [
"protoc",
"-I.", # Look for proto files in the current directory
f"--openapiv2_out={output_dir}", # Output directory for OpenAPI spec
"--openapiv2_opt=logtostderr=true", # Optional: log to stderr for debugging
proto_file
]
print(f"Executing command: {' '.join(protoc_command)}\n")
try:
result = subprocess.run(protoc_command, check=True, capture_output=True, text=True)
print("STDOUT:\n", result.stdout)
if result.stderr:
print("STDERR:\n", result.stderr) # Often contains warnings or logs from the plugin
print(f"\nSuccessfully generated OpenAPI v2 spec at: {swagger_output_file}")
except subprocess.CalledProcessError as e:
print(f"Error generating OpenAPI spec: {e}", file=sys.stderr)
print(f"Command: {' '.join(e.cmd)}", file=sys.stderr)
print(f"STDOUT:\n{e.stdout}", file=sys.stderr)
print(f"STDERR:\n{e.stderr}", file=sys.stderr)
sys.exit(1)
except FileNotFoundError as e:
print(f"Error: Command not found. {e}", file=sys.stderr)
sys.exit(1)
finally:
# --- Clean up ---
if os.path.exists(proto_file):
os.remove(proto_file)
print(f"\nCleaned up temporary '{proto_file}'. The output spec remains in '{output_dir}'.")