Keep KTX seat lookup typing from obscuring review signals

The seat inspection follow-up already fixed the runtime schema issues, but review still had changed-area pyright noise around the dynamic Korail boundaries. Route those calls through explicit narrow dynamic seams so the remaining pyright output stays confined to the existing baseline.\n\nConstraint: PR #296 follow-up must preserve the existing korail2 dynamic runtime behavior while cleaning the reviewed changed area.\nRejected: Broad pyright baseline cleanup | outside issue #292 scope and higher risk for this follow-up.\nConfidence: high\nScope-risk: narrow\nDirective: Keep future Korail mobile endpoint additions behind explicit typed/dynamic boundaries so changed-area diagnostics remain actionable.\nTested: PYTHONPATH=.:scripts PYTHONNOUSERSITE=1 python3 -m unittest discover -s scripts -p 'test_ktx_booking.py'; PYTHONPATH=.:scripts python3 -m unittest scripts.test_ktx_booking; python3 -m py_compile scripts/ktx_booking.py scripts/test_ktx_booking.py; node --check scripts/skill-docs.test.js; node --test scripts/skill-docs.test.js; git diff --check origin/dev...HEAD; ruff check scripts/ktx_booking.py scripts/test_ktx_booking.py; PYTHONPATH=.:scripts python3 scripts/ktx_booking.py seats --help; pyright changed-area grep against search/seat lines\nNot-tested: live authenticated Korail seat lookup; full pyright remains on existing dynamic Korail/test-helper baseline
This commit is contained in:
Jeffrey (Dongkyu) Kim 2026-05-29 01:30:33 +09:00
commit 1d4b2eb723

View file

@ -11,6 +11,7 @@ import string
import sys
import time
from functools import reduce
from typing import Any
try:
from Crypto.Cipher import AES
@ -366,6 +367,12 @@ class PatchedKorail(Korail):
if auto_login:
self.login(korail_id, korail_pw)
def _result_check(self, data: dict[str, Any]) -> bool:
result_check = getattr(Korail, "_result_check", None)
if not callable(result_check):
raise KorailError("korail2 result checker is unavailable")
return bool(result_check(self, data))
def _generate_sid(self, timestamp_ms: int) -> str:
ensure_runtime_dependencies()
plaintext = f"{self._device}{timestamp_ms}".encode("utf-8")
@ -436,7 +443,8 @@ class PatchedKorail(Korail):
include_no_seats: bool = False,
include_waiting_list: bool = False,
):
kst_now = korail_mod.datetime.now(korail_mod.timezone.utc) + korail_mod.timedelta(hours=9)
korail_api: Any = korail_mod
kst_now = korail_api.datetime.now(korail_api.timezone.utc) + korail_api.timedelta(hours=9)
if date is None:
date = kst_now.strftime("%Y%m%d")
if time_value is None:
@ -444,17 +452,20 @@ class PatchedKorail(Korail):
if passengers is None:
passengers = [AdultPassenger()]
passengers = Passenger.reduce(passengers)
adult_count = reduce(lambda total, passenger: total + passenger.count, [p for p in passengers if isinstance(p, AdultPassenger)], 0)
child_count = reduce(lambda total, passenger: total + passenger.count, [p for p in passengers if isinstance(p, ChildPassenger)], 0)
reduced_passengers = Passenger.reduce(passengers)
if reduced_passengers is None:
raise KorailError("korail passenger reduction failed")
passenger_list: list[Any] = list(reduced_passengers)
adult_count = reduce(lambda total, passenger: total + passenger.count, [p for p in passenger_list if isinstance(p, AdultPassenger)], 0)
child_count = reduce(lambda total, passenger: total + passenger.count, [p for p in passenger_list if isinstance(p, ChildPassenger)], 0)
toddler_count = reduce(
lambda total, passenger: total + passenger.count,
[p for p in passengers if isinstance(p, ToddlerPassenger)],
[p for p in passenger_list if isinstance(p, ToddlerPassenger)],
0,
)
senior_count = reduce(lambda total, passenger: total + passenger.count, [p for p in passengers if isinstance(p, SeniorPassenger)], 0)
senior_count = reduce(lambda total, passenger: total + passenger.count, [p for p in passenger_list if isinstance(p, SeniorPassenger)], 0)
headers, sid = self._auth_headers_and_sid(korail_mod.KORAIL_SEARCH_SCHEDULE)
headers, sid = self._auth_headers_and_sid(korail_api.KORAIL_SEARCH_SCHEDULE)
payload = {
"Device": self._device,
"radJobId": "1",
@ -482,13 +493,13 @@ class PatchedKorail(Korail):
if sid:
payload["Sid"] = sid
response = self._session.post(korail_mod.KORAIL_SEARCH_SCHEDULE, params=payload, headers=headers)
response = self._session.post(korail_api.KORAIL_SEARCH_SCHEDULE, params=payload, headers=headers)
data = json.loads(response.text)
if self._result_check(data):
train_infos = data["trn_infos"]["trn_info"]
if isinstance(train_infos, dict):
train_infos = [train_infos]
details = [(korail_mod.Train(info), info) for info in train_infos]
details = [(korail_api.Train(info), info) for info in train_infos]
details = [(train, info) for train, info in details if train.dep_name == dep and train.arr_name == arr]
filters = [lambda train: train.has_seat()]
if include_no_seats:
@ -499,6 +510,7 @@ class PatchedKorail(Korail):
if not details:
raise NoResultsError()
return details
raise NoResultsError()
def search_train(
self,