334 lines
10 KiB
Python
Executable File
334 lines
10 KiB
Python
Executable File
import platform
|
|
import subprocess
|
|
import tempfile
|
|
import textwrap
|
|
import urllib.request
|
|
import zipfile
|
|
from pathlib import Path
|
|
from typing import Literal
|
|
|
|
SYSTEM = platform.system()
|
|
MACHINE = platform.machine()
|
|
CACHE_DIR = Path(".cache/protobuf_cache")
|
|
MLFLOW_PROTOS_DIR = Path("mlflow/protos")
|
|
TEST_PROTOS_DIR = Path("tests/protos")
|
|
OTEL_PROTOS_DIR = Path("mlflow/protos/opentelemetry")
|
|
|
|
|
|
def gen_protos(
|
|
proto_dir: Path,
|
|
proto_files: list[Path],
|
|
lang: Literal["python", "java"],
|
|
protoc_bin: Path,
|
|
protoc_include_paths: list[Path],
|
|
out_dir: Path,
|
|
) -> None:
|
|
assert lang in ["python", "java"]
|
|
out_dir.mkdir(parents=True, exist_ok=True)
|
|
subprocess.check_call([
|
|
protoc_bin,
|
|
"--fatal_warnings",
|
|
*(f"-I={p}" for p in protoc_include_paths),
|
|
f"-I={proto_dir}",
|
|
f"--{lang}_out={out_dir}",
|
|
*[proto_dir / pf for pf in proto_files],
|
|
])
|
|
|
|
|
|
def gen_stub_files(
|
|
proto_dir: Path,
|
|
proto_files: list[Path],
|
|
protoc_bin: Path,
|
|
protoc_include_paths: list[Path],
|
|
out_dir: Path,
|
|
) -> None:
|
|
out_dir.mkdir(parents=True, exist_ok=True)
|
|
subprocess.check_call([
|
|
protoc_bin,
|
|
"--fatal_warnings",
|
|
*(f"-I={p}" for p in protoc_include_paths),
|
|
f"-I={proto_dir}",
|
|
f"--pyi_out={out_dir}",
|
|
*[proto_dir / pf for pf in proto_files],
|
|
])
|
|
|
|
|
|
def gen_proto_docs(
|
|
proto_dir: Path,
|
|
proto_files: list[Path],
|
|
protoc_bin: Path,
|
|
protoc_include_path: Path,
|
|
out_dir: Path,
|
|
) -> None:
|
|
plugin_path = Path("dev/proto-plugin.sh").resolve()
|
|
subprocess.check_call([
|
|
protoc_bin,
|
|
f"-I={protoc_include_path}",
|
|
f"-I={proto_dir}",
|
|
f"--plugin=protoc-gen-doc={plugin_path}",
|
|
f"--doc_out={out_dir}",
|
|
*[proto_dir / pf for pf in proto_files],
|
|
])
|
|
|
|
|
|
def apply_python_gencode_replacement(file_path: Path) -> None:
|
|
content = file_path.read_text()
|
|
|
|
for old, new in python_gencode_replacements:
|
|
content = content.replace(old, new)
|
|
|
|
file_path.write_text(content, encoding="UTF-8")
|
|
|
|
|
|
def _get_python_output_path(proto_file_path: Path) -> Path:
|
|
return proto_file_path.parent / (proto_file_path.stem + "_pb2.py")
|
|
|
|
|
|
def to_paths(*args: str) -> list[Path]:
|
|
return list(map(Path, args))
|
|
|
|
|
|
basic_proto_files = to_paths(
|
|
"databricks.proto",
|
|
"service.proto",
|
|
"model_registry.proto",
|
|
"databricks_artifacts.proto",
|
|
"mlflow_artifacts.proto",
|
|
"internal.proto",
|
|
"scalapb/scalapb.proto",
|
|
"assessments.proto",
|
|
"datasets.proto",
|
|
"issues.proto",
|
|
"label_schemas.proto",
|
|
"review_queues.proto",
|
|
"webhooks.proto",
|
|
"jobs.proto",
|
|
"prompt_optimization.proto",
|
|
)
|
|
uc_proto_files = to_paths(
|
|
"databricks_managed_catalog_messages.proto",
|
|
"databricks_managed_catalog_service.proto",
|
|
"databricks_uc_registry_messages.proto",
|
|
"databricks_uc_registry_service.proto",
|
|
"databricks_filesystem_service.proto",
|
|
"unity_catalog_oss_messages.proto",
|
|
"unity_catalog_oss_service.proto",
|
|
"unity_catalog_prompt_messages.proto",
|
|
"unity_catalog_prompt_service.proto",
|
|
)
|
|
tracing_proto_files = to_paths(
|
|
"databricks_exception_with_details.proto",
|
|
"databricks_tracing.proto",
|
|
)
|
|
facet_proto_files = to_paths("facet_feature_statistics.proto")
|
|
python_proto_files = basic_proto_files + uc_proto_files + facet_proto_files + tracing_proto_files
|
|
test_proto_files = to_paths("test_message.proto")
|
|
|
|
|
|
python_gencode_replacements = [
|
|
(
|
|
"from scalapb import scalapb_pb2 as scalapb_dot_scalapb__pb2",
|
|
"from .scalapb import scalapb_pb2 as scalapb_dot_scalapb__pb2",
|
|
),
|
|
(
|
|
"import databricks_pb2 as databricks__pb2",
|
|
"from . import databricks_pb2 as databricks__pb2",
|
|
),
|
|
(
|
|
"import databricks_uc_registry_messages_pb2 as databricks__uc__registry__messages__pb2",
|
|
"from . import databricks_uc_registry_messages_pb2 as databricks_uc_registry_messages_pb2",
|
|
),
|
|
(
|
|
"import databricks_managed_catalog_messages_pb2 as databricks__managed__catalog__"
|
|
"messages__pb2",
|
|
"from . import databricks_managed_catalog_messages_pb2 as databricks_managed_"
|
|
"catalog_messages_pb2",
|
|
),
|
|
(
|
|
"import unity_catalog_oss_messages_pb2 as unity__catalog__oss__messages__pb2",
|
|
"from . import unity_catalog_oss_messages_pb2 as unity_catalog_oss_messages_pb2",
|
|
),
|
|
(
|
|
"import unity_catalog_prompt_messages_pb2 as unity__catalog__prompt__messages__pb2",
|
|
"from . import unity_catalog_prompt_messages_pb2 as unity_catalog_prompt_messages_pb2",
|
|
),
|
|
(
|
|
"import service_pb2 as service__pb2",
|
|
"from . import service_pb2 as service__pb2",
|
|
),
|
|
(
|
|
"import assessments_pb2 as assessments__pb2",
|
|
"from . import assessments_pb2 as assessments__pb2",
|
|
),
|
|
(
|
|
"import datasets_pb2 as datasets__pb2",
|
|
"from . import datasets_pb2 as datasets__pb2",
|
|
),
|
|
(
|
|
"import issues_pb2 as issues__pb2",
|
|
"from . import issues_pb2 as issues__pb2",
|
|
),
|
|
(
|
|
"import label_schemas_pb2 as label__schemas__pb2",
|
|
"from . import label_schemas_pb2 as label__schemas__pb2",
|
|
),
|
|
(
|
|
"import review_queues_pb2 as review__queues__pb2",
|
|
"from . import review_queues_pb2 as review__queues__pb2",
|
|
),
|
|
(
|
|
"import webhooks_pb2 as webhooks__pb2",
|
|
"from . import webhooks_pb2 as webhooks__pb2",
|
|
),
|
|
(
|
|
"import jobs_pb2 as jobs__pb2",
|
|
"from . import jobs_pb2 as jobs__pb2",
|
|
),
|
|
(
|
|
"import prompt_optimization_pb2 as prompt__optimization__pb2",
|
|
"from . import prompt_optimization_pb2 as prompt__optimization__pb2",
|
|
),
|
|
(
|
|
"import databricks_exception_with_details_pb2 as databricks__exception__with__details__pb2",
|
|
"from . import databricks_exception_with_details_pb2 as databricks_exception_"
|
|
"with_details_pb2",
|
|
),
|
|
]
|
|
|
|
|
|
def gen_python_protos(protoc_bin: Path, protoc_include_paths: list[Path], out_dir: Path) -> None:
|
|
gen_protos(
|
|
MLFLOW_PROTOS_DIR,
|
|
python_proto_files,
|
|
"python",
|
|
protoc_bin,
|
|
protoc_include_paths,
|
|
out_dir,
|
|
)
|
|
|
|
gen_protos(
|
|
TEST_PROTOS_DIR,
|
|
test_proto_files,
|
|
"python",
|
|
protoc_bin,
|
|
protoc_include_paths,
|
|
out_dir,
|
|
)
|
|
|
|
for proto_file in python_proto_files:
|
|
apply_python_gencode_replacement(out_dir / _get_python_output_path(proto_file))
|
|
|
|
|
|
def download_file(url: str, output_path: Path) -> None:
|
|
urllib.request.urlretrieve(url, output_path)
|
|
|
|
|
|
def download_and_extract_protoc(version: Literal["3.19.4", "26.0"]) -> tuple[Path, Path]:
|
|
"""
|
|
Download and extract specific version protoc tool for Linux systems,
|
|
return extracted protoc executable file path and include path.
|
|
"""
|
|
assert SYSTEM == "Linux", "This script only supports Linux systems."
|
|
assert MACHINE in ["x86_64", "aarch64"], (
|
|
"This script only supports x86_64 or aarch64 CPU architectures."
|
|
)
|
|
|
|
cpu_type = "x86_64" if MACHINE == "x86_64" else "aarch_64"
|
|
protoc_zip_filename = f"protoc-{version}-linux-{cpu_type}.zip"
|
|
|
|
downloaded_protoc_bin = CACHE_DIR / f"protoc-{version}" / "bin" / "protoc"
|
|
downloaded_protoc_include_path = CACHE_DIR / f"protoc-{version}" / "include"
|
|
if not (downloaded_protoc_bin.is_file() and downloaded_protoc_include_path.is_dir()):
|
|
with tempfile.TemporaryDirectory() as t:
|
|
zip_path = Path(t) / protoc_zip_filename
|
|
download_file(
|
|
f"https://github.com/protocolbuffers/protobuf/releases/download/v{version}/{protoc_zip_filename}",
|
|
zip_path,
|
|
)
|
|
with zipfile.ZipFile(zip_path, "r") as zip_ref:
|
|
zip_ref.extractall(CACHE_DIR / f"protoc-{version}")
|
|
|
|
# Make protoc executable
|
|
downloaded_protoc_bin.chmod(0o755)
|
|
return downloaded_protoc_bin, downloaded_protoc_include_path
|
|
|
|
|
|
def generate_final_python_gencode(
|
|
gencode3194_path: Path, gencode5260_path: Path, out_path: Path
|
|
) -> None:
|
|
gencode3194 = gencode3194_path.read_text()
|
|
gencode5260 = gencode5260_path.read_text()
|
|
|
|
merged_code = f"""
|
|
import google.protobuf
|
|
from packaging.version import Version
|
|
if Version(google.protobuf.__version__).major >= 5:
|
|
{textwrap.indent(gencode5260, " ")}
|
|
else:
|
|
{textwrap.indent(gencode3194, " ")}
|
|
"""
|
|
out_path.write_text(merged_code, encoding="UTF-8")
|
|
|
|
|
|
def main() -> None:
|
|
CACHE_DIR.mkdir(parents=True, exist_ok=True)
|
|
with tempfile.TemporaryDirectory() as temp_gencode_dir:
|
|
temp_gencode_path = Path(temp_gencode_dir)
|
|
proto3194_out = temp_gencode_path / "3.19.4"
|
|
proto5260_out = temp_gencode_path / "26.0"
|
|
proto3194_out.mkdir(exist_ok=True)
|
|
proto5260_out.mkdir(exist_ok=True)
|
|
|
|
protoc3194, protoc3194_include = download_and_extract_protoc("3.19.4")
|
|
protoc5260, protoc5260_include = download_and_extract_protoc("26.0")
|
|
|
|
# Build include paths list
|
|
protoc3194_includes = [protoc3194_include, OTEL_PROTOS_DIR]
|
|
protoc5260_includes = [protoc5260_include, OTEL_PROTOS_DIR]
|
|
|
|
gen_python_protos(protoc3194, protoc3194_includes, proto3194_out)
|
|
gen_python_protos(protoc5260, protoc5260_includes, proto5260_out)
|
|
|
|
for proto_files, protos_dir in [
|
|
(python_proto_files, MLFLOW_PROTOS_DIR),
|
|
(test_proto_files, TEST_PROTOS_DIR),
|
|
]:
|
|
for proto_file in proto_files:
|
|
gencode_path = _get_python_output_path(proto_file)
|
|
|
|
generate_final_python_gencode(
|
|
proto3194_out / gencode_path,
|
|
proto5260_out / gencode_path,
|
|
protos_dir / gencode_path,
|
|
)
|
|
|
|
# generate java gencode using pinned protoc 3.19.4 version.
|
|
gen_protos(
|
|
MLFLOW_PROTOS_DIR,
|
|
basic_proto_files,
|
|
"java",
|
|
protoc3194,
|
|
protoc3194_includes,
|
|
Path("mlflow/java/client/src/main/java"),
|
|
)
|
|
|
|
gen_stub_files(
|
|
MLFLOW_PROTOS_DIR,
|
|
python_proto_files,
|
|
protoc5260,
|
|
protoc5260_includes,
|
|
Path("mlflow/protos/"),
|
|
)
|
|
|
|
gen_proto_docs(
|
|
MLFLOW_PROTOS_DIR,
|
|
basic_proto_files,
|
|
protoc5260,
|
|
protoc5260_include,
|
|
Path("mlflow/protos"),
|
|
)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|