Coverage for brokers / korea_investment / korea_invest_url_provider.py: 100%
33 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-04 15:08 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-04-04 15:08 +0000
1# core/korea_invest_url_provider.py
2from __future__ import annotations
3from typing import Mapping, Iterable, Optional, Callable, Union
4from enum import Enum
5from urllib.parse import urljoin
6from config.config_loader import load_configs, load_config, KIS_CONFIG_PATH
7from brokers.korea_investment.korea_invest_env import KoreaInvestApiEnv
10class KoreaInvestUrlProvider:
11 """
12 kis_config.yaml(paths) + base_url을 바탕으로 엔드포인트 절대 URL을 만들어주는 경량 Provider.
13 - 기본적으로 kis_config.yaml 만 읽어 경로 테이블(paths)을 구성
14 - base_url은 생성자 인자로 주입 (권장)하거나, load_configs() 병합 결과에 존재하면 자동 사용
15 """
16 def __init__(self, get_base_url: Callable[[], str], paths: Mapping[str, str]) -> None:
17 self._get_base_url = get_base_url
18 self._paths = dict(paths or {})
20 @classmethod
21 def from_env_and_kis_config(
22 cls, env: KoreaInvestApiEnv,
23 kis_config_override: Optional[dict] = None,
24 get_base_url_override: Optional[Callable[[], str]] = None,
25 ) -> "KoreaInvestUrlProvider":
26 kis_conf = kis_config_override if kis_config_override is not None else load_config(KIS_CONFIG_PATH)
27 paths = kis_conf.get("paths")
28 if not isinstance(paths, dict) or not paths:
29 raise ValueError("kis_config.yaml의 paths가 없거나 비었습니다.")
30 get_base_url = get_base_url_override or env.get_base_url
31 return cls(get_base_url=get_base_url, paths=paths)
34 # ---------- 조회 ----------
35 def has(self, key: str) -> bool:
36 return key in self._paths
38 def keys(self) -> Iterable[str]:
39 return self._paths.keys()
41 def path(self, key: str) -> str:
42 if key not in self._paths:
43 raise KeyError(f"kis_config.paths에 '{key}'가 없습니다.")
44 return self._paths[key]
46 def url(self, key_or_path: Union[str, Enum]) -> str:
47 base = (self._get_base_url() or "").rstrip("/")
48 if not base:
49 raise ValueError("env.get_base_url()이 빈 값입니다. env.set_trading_mode(...)로 활성화했는지 확인하세요.")
50 # Enum이면 .value 사용
51 key_str = key_or_path.value if isinstance(key_or_path, Enum) else str(key_or_path)
52 rel = self.path(key_str) if self.has(key_str) else key_str
53 return urljoin(base + "/", rel.lstrip("/"))