| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061 |
- #!/usr/bin/env python3
- """Minimal PyODPS client configured only through environment variables."""
- from __future__ import annotations
- import os
- from pathlib import Path
- from odps import ODPS, options
- ENV_ACCESS_ID = "ODPS_ACCESS_ID"
- ENV_ACCESS_SECRET = "ODPS_ACCESS_SECRET"
- ENV_PROJECT = "ODPS_PROJECT"
- ENV_ENDPOINT = "ODPS_ENDPOINT"
- def require_env(name: str) -> str:
- value = os.getenv(name)
- if not value:
- raise RuntimeError(f"Missing required environment variable: {name}")
- return value
- class ODPSClient:
- def __init__(self, project: str | None = None, endpoint: str | None = None):
- access_id = require_env(ENV_ACCESS_ID)
- access_secret = require_env(ENV_ACCESS_SECRET)
- project_name = project or require_env(ENV_PROJECT)
- endpoint_url = endpoint or require_env(ENV_ENDPOINT)
- options.connect_timeout = 60
- options.read_timeout = 1200
- options.retry_times = 3
- self.odps = ODPS(
- access_id,
- access_secret,
- project=project_name,
- endpoint=endpoint_url,
- )
- def execute_sql(self, sql: str):
- if not sql.strip():
- raise ValueError("SQL must not be empty")
- instance = self.odps.run_sql(
- sql,
- hints={"odps.sql.submit.mode": "script"},
- )
- print(f"[ODPS] InstanceId: {instance.id}", flush=True)
- instance.wait_for_success()
- with instance.open_reader(tunnel=True) as reader:
- return reader.to_pandas()
- def execute_sql_result_save_file(self, sql: str, output_file: str | Path):
- output_path = Path(output_file).expanduser()
- output_path.parent.mkdir(parents=True, exist_ok=True)
- data = self.execute_sql(sql)
- data.to_csv(output_path, index=False, encoding="utf-8-sig")
- return output_path
|