auto: 일일 백업 2026-06-19 02:00
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -13,9 +13,12 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import http.client
|
||||
import socket
|
||||
import sys
|
||||
import threading
|
||||
import time
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
from datetime import datetime, timezone, timedelta
|
||||
from pathlib import Path
|
||||
@@ -48,6 +51,8 @@ TR_MINUTE_CHART = 'ka10080' # 주식분봉차트조회 (1/3/5/10/15/30/45/60분
|
||||
TR_INVESTOR_FLOW = 'ka10059' # 종목별투자자기관별 (외국인·기관 일별 매매)
|
||||
|
||||
STOCK_CODES_CACHE = STATE_DIR / 'stock_codes.json'
|
||||
_HTTP_TRANSIENT_RETRIES = 2
|
||||
_HTTP_TRANSIENT_BACKOFF_SEC = 0.8
|
||||
|
||||
|
||||
def load_credentials() -> dict:
|
||||
@@ -86,13 +91,28 @@ def _http_post_full(url: str, body: dict, headers: dict | None = None) -> tuple[
|
||||
if headers:
|
||||
h.update(headers)
|
||||
req = urllib.request.Request(url, data=data, method='POST', headers=h)
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=15) as r:
|
||||
resp_headers = {k.lower(): v for k, v in r.headers.items()}
|
||||
return json.loads(r.read().decode('utf-8', 'ignore')), resp_headers
|
||||
except urllib.error.HTTPError as e:
|
||||
body_text = e.read().decode('utf-8', 'ignore')
|
||||
raise RuntimeError(f'HTTP {e.code} {url}: {body_text}') from e
|
||||
transient_errors = (
|
||||
ConnectionResetError,
|
||||
TimeoutError,
|
||||
socket.timeout,
|
||||
urllib.error.URLError,
|
||||
http.client.RemoteDisconnected,
|
||||
http.client.IncompleteRead,
|
||||
)
|
||||
for attempt in range(_HTTP_TRANSIENT_RETRIES + 1):
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=15) as r:
|
||||
resp_headers = {k.lower(): v for k, v in r.headers.items()}
|
||||
return json.loads(r.read().decode('utf-8', 'ignore')), resp_headers
|
||||
except urllib.error.HTTPError as e:
|
||||
body_text = e.read().decode('utf-8', 'ignore')
|
||||
raise RuntimeError(f'HTTP {e.code} {url}: {body_text}') from e
|
||||
except transient_errors:
|
||||
if attempt >= _HTTP_TRANSIENT_RETRIES:
|
||||
raise
|
||||
time.sleep(_HTTP_TRANSIENT_BACKOFF_SEC * (attempt + 1))
|
||||
|
||||
raise RuntimeError(f'HTTP POST failed unexpectedly: {url}')
|
||||
|
||||
|
||||
def _token_cache_path(label: str) -> Path:
|
||||
|
||||
Reference in New Issue
Block a user