kiwoom-cli 2.2.0__tar.gz → 2.2.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/PKG-INFO +1 -1
  2. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/kiwoom_cli/__init__.py +1 -1
  3. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/kiwoom_cli/client.py +6 -0
  4. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/kiwoom_cli/main.py +17 -1
  5. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/kiwoom_cli.egg-info/PKG-INFO +1 -1
  6. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/tests/test_auth_cli.py +56 -0
  7. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/LICENSE +0 -0
  8. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/README.md +0 -0
  9. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/kiwoom_cli/api_spec.py +0 -0
  10. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/kiwoom_cli/auth.py +0 -0
  11. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/kiwoom_cli/commands/__init__.py +0 -0
  12. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/kiwoom_cli/commands/_constants.py +0 -0
  13. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/kiwoom_cli/commands/account.py +0 -0
  14. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/kiwoom_cli/commands/dashboard.py +0 -0
  15. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/kiwoom_cli/commands/market.py +0 -0
  16. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/kiwoom_cli/commands/order.py +0 -0
  17. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/kiwoom_cli/commands/stock.py +0 -0
  18. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/kiwoom_cli/commands/stream.py +0 -0
  19. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/kiwoom_cli/commands/us/__init__.py +0 -0
  20. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/kiwoom_cli/commands/us/_constants.py +0 -0
  21. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/kiwoom_cli/commands/us/account_ops.py +0 -0
  22. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/kiwoom_cli/commands/us/detect.py +0 -0
  23. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/kiwoom_cli/commands/us/exchange.py +0 -0
  24. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/kiwoom_cli/commands/us/order_ops.py +0 -0
  25. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/kiwoom_cli/commands/us/stock_ops.py +0 -0
  26. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/kiwoom_cli/commands/watch.py +0 -0
  27. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/kiwoom_cli/config.py +0 -0
  28. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/kiwoom_cli/formatters.py +0 -0
  29. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/kiwoom_cli/output.py +0 -0
  30. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/kiwoom_cli/streaming.py +0 -0
  31. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/kiwoom_cli.egg-info/SOURCES.txt +0 -0
  32. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/kiwoom_cli.egg-info/dependency_links.txt +0 -0
  33. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/kiwoom_cli.egg-info/entry_points.txt +0 -0
  34. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/kiwoom_cli.egg-info/requires.txt +0 -0
  35. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/kiwoom_cli.egg-info/top_level.txt +0 -0
  36. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/pyproject.toml +0 -0
  37. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/setup.cfg +0 -0
  38. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/tests/test_account.py +0 -0
  39. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/tests/test_auth.py +0 -0
  40. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/tests/test_cli.py +0 -0
  41. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/tests/test_client.py +0 -0
  42. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/tests/test_credentials.py +0 -0
  43. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/tests/test_formatters.py +0 -0
  44. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/tests/test_formatters_property.py +0 -0
  45. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/tests/test_json_purity.py +0 -0
  46. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/tests/test_market.py +0 -0
  47. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/tests/test_migration.py +0 -0
  48. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/tests/test_order.py +0 -0
  49. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/tests/test_stock.py +0 -0
  50. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/tests/test_us.py +0 -0
  51. {kiwoom_cli-2.2.0 → kiwoom_cli-2.2.1}/tests/test_us_account.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kiwoom-cli
3
- Version: 2.2.0
3
+ Version: 2.2.1
4
4
  Summary: User-friendly CLI for the Kiwoom Securities REST API (키움증권)
5
5
  License: LicenseRef-kiwoom-cli-Source-Available-1.0
6
6
  Project-URL: Homepage, https://github.com/gejyn14/kiwoom-cli
@@ -1,3 +1,3 @@
1
1
  """Kiwoom Securities REST API CLI."""
2
2
 
3
- __version__ = "2.2.0"
3
+ __version__ = "2.2.1"
@@ -24,6 +24,10 @@ class KiwoomAPIError(Exception):
24
24
  super().__init__(f"[{code}] {msg}")
25
25
 
26
26
 
27
+ class KiwoomAuthError(Exception):
28
+ """No access token available — the caller must authenticate first."""
29
+
30
+
27
31
  class KiwoomClient:
28
32
  """Synchronous client for the Kiwoom REST API."""
29
33
 
@@ -80,6 +84,8 @@ class KiwoomClient:
80
84
  next_key: str = "",
81
85
  ) -> tuple[dict[str, Any], dict[str, str]]:
82
86
  """Make a single API request. Returns (body_json, response_headers)."""
87
+ if not self.token:
88
+ raise KiwoomAuthError()
83
89
  url_path = get_url(api_id)
84
90
  headers = self._headers(api_id, cont_yn, next_key)
85
91
  if self._should_spin():
@@ -7,9 +7,10 @@ import os
7
7
 
8
8
  import click
9
9
  import httpx
10
+ import keyring
10
11
 
11
12
  from . import __version__, auth, config
12
- from .client import KiwoomClient, KiwoomAPIError
13
+ from .client import KiwoomClient, KiwoomAPIError, KiwoomAuthError
13
14
  from .commands.account import account
14
15
  from .commands.dashboard import dashboard
15
16
  from .commands.market import market
@@ -45,6 +46,21 @@ class KiwoomGroup(click.Group):
45
46
  else:
46
47
  console.print(f"[red]API 오류:[/] {e}")
47
48
  ctx.exit(EXIT_API)
49
+ except KiwoomAuthError:
50
+ fmt = ctx.obj.get("format", "table") if ctx.obj else "table"
51
+ msg = "토큰이 없습니다. 'kiwoom auth login'으로 발급하세요."
52
+ if fmt == "json":
53
+ click.echo(json.dumps({"error": msg, "code": "auth_required"}, ensure_ascii=False))
54
+ else:
55
+ console.print("[red]인증 필요:[/] 토큰이 없습니다. [dim]kiwoom auth login[/]")
56
+ ctx.exit(EXIT_AUTH)
57
+ except keyring.errors.KeyringError:
58
+ console.print("[red]키체인 오류:[/] OS 키체인에 접근할 수 없습니다 (잠김 또는 비대화형 세션).")
59
+ console.print(
60
+ "[dim]키체인 없는 환경에서는 본인 터미널에서 토큰을 발급한 뒤 "
61
+ "KIWOOM_TOKEN 환경변수로 전달하세요. (README '샌드박스 환경' 참고)[/]"
62
+ )
63
+ ctx.exit(EXIT_INPUT)
48
64
  except httpx.HTTPStatusError as e:
49
65
  if e.response.status_code == 401:
50
66
  console.print("[red]인증 오류:[/] 토큰이 만료되었습니다. [dim]kiwoom auth login[/]")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: kiwoom-cli
3
- Version: 2.2.0
3
+ Version: 2.2.1
4
4
  Summary: User-friendly CLI for the Kiwoom Securities REST API (키움증권)
5
5
  License: LicenseRef-kiwoom-cli-Source-Available-1.0
6
6
  Project-URL: Homepage, https://github.com/gejyn14/kiwoom-cli
@@ -234,3 +234,59 @@ def test_auth_status_reflects_env_token_with_broken_keyring(monkeypatch):
234
234
  result = runner.invoke(cli, ["auth", "status"])
235
235
  assert result.exit_code == 0
236
236
  assert "KIWOOM_TOKEN" in result.output
237
+
238
+
239
+ # ── 키체인 쓰기 실패 / 토큰 부재 처리 ─────────────────────
240
+
241
+
242
+ def test_config_setup_keychain_write_failure_friendly_error(monkeypatch, mem_keyring):
243
+ """키체인 쓰기 불가(-25308 등): traceback 대신 친절한 안내 + exit 1."""
244
+
245
+ def _locked(svc, key, val):
246
+ raise keyring.errors.KeyringError("errSecInteractionNotAllowed (-25308)")
247
+
248
+ monkeypatch.setattr(keyring, "set_password", _locked)
249
+ runner = CliRunner()
250
+ result = runner.invoke(
251
+ cli, ["config", "setup"], input="ak\nsk\nmock\n\n\n"
252
+ )
253
+ assert result.exit_code == 1, result.output
254
+ # 예외가 새어나오지 않고 CLI가 스스로 종료해야 한다
255
+ assert result.exception is None or isinstance(result.exception, SystemExit)
256
+ combined = result.output + result.stderr
257
+ assert "키체인" in combined
258
+ assert "KIWOOM_TOKEN" in combined
259
+
260
+
261
+ def test_missing_token_exits_3_before_any_request(monkeypatch, mem_keyring):
262
+ """토큰이 전혀 없으면 요청을 보내지 않고 exit 3 + auth login 힌트."""
263
+ import httpx
264
+
265
+ monkeypatch.delenv("KIWOOM_TOKEN", raising=False)
266
+
267
+ def _no_network(*args, **kwargs):
268
+ raise AssertionError("token 없이 HTTP 요청이 나가면 안 된다")
269
+
270
+ monkeypatch.setattr(httpx.Client, "post", _no_network)
271
+ runner = CliRunner()
272
+ result = runner.invoke(cli, ["stock", "info", "005930"])
273
+ assert result.exit_code == 3, result.output
274
+ assert "auth login" in result.output + result.stderr
275
+
276
+
277
+ def test_missing_token_json_mode_single_json_error_doc(monkeypatch, mem_keyring):
278
+ """-f json + 토큰 부재: stdout은 단일 JSON 에러 문서, exit 3."""
279
+ import json as _json
280
+
281
+ import httpx
282
+
283
+ monkeypatch.delenv("KIWOOM_TOKEN", raising=False)
284
+ monkeypatch.setattr(
285
+ httpx.Client, "post",
286
+ lambda *a, **k: (_ for _ in ()).throw(AssertionError("no request expected")),
287
+ )
288
+ runner = CliRunner()
289
+ result = runner.invoke(cli, ["-f", "json", "stock", "info", "005930"])
290
+ assert result.exit_code == 3, result.output
291
+ doc = _json.loads(result.stdout)
292
+ assert "error" in doc
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes