qubecli 1.0.0__py3-none-any.whl

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.
qubecli/__init__.py ADDED
File without changes
qubecli/main.py ADDED
@@ -0,0 +1,186 @@
1
+ import json
2
+ from contextlib import contextmanager
3
+ from importlib.metadata import version
4
+ from pathlib import Path
5
+ from typing import Annotated, Generator, Optional
6
+
7
+ import grpc
8
+ import typer
9
+ from qubecore_client import QubeClient
10
+
11
+ _CONFIG_PATH = Path.home() / ".qubecore" / "config.json"
12
+
13
+ app = typer.Typer(help="QubeCore CLI — Quantum Computing Operating System")
14
+ config_app = typer.Typer(help="서버 설정 관리")
15
+ app.add_typer(config_app, name="config")
16
+
17
+
18
+ class _State:
19
+ address: str = "localhost:50051"
20
+
21
+
22
+ _state = _State()
23
+
24
+
25
+ def _load_config() -> dict:
26
+ if _CONFIG_PATH.exists():
27
+ return json.loads(_CONFIG_PATH.read_text())
28
+ return {}
29
+
30
+
31
+ def _save_config(data: dict) -> None:
32
+ _CONFIG_PATH.parent.mkdir(parents=True, exist_ok=True)
33
+ _CONFIG_PATH.write_text(json.dumps(data, indent=2))
34
+
35
+
36
+ def _resolve_address(host: Optional[str]) -> str:
37
+ if host is not None:
38
+ return host
39
+ return _load_config().get("host", "localhost:50051")
40
+
41
+
42
+ @contextmanager
43
+ def _get_client(address: str) -> Generator[QubeClient, None, None]:
44
+ client = QubeClient(address=address)
45
+ try:
46
+ yield client
47
+ except grpc.RpcError as e:
48
+ if e.code() == grpc.StatusCode.UNAVAILABLE:
49
+ typer.echo(f"서버에 연결할 수 없습니다: {address}", err=True)
50
+ typer.echo(" 'qubecore config set host <host:port>' 로 서버 주소를 확인하세요.", err=True)
51
+ else:
52
+ typer.echo(f"gRPC 오류: {e.details()}", err=True)
53
+ raise typer.Exit(1)
54
+ finally:
55
+ client.close()
56
+
57
+
58
+ def _priority_label(priority: int) -> str:
59
+ return {1: "CRITICAL", 2: "HIGH", 3: "NORMAL", 4: "LOW"}.get(priority, str(priority))
60
+
61
+
62
+ @app.callback()
63
+ def _callback(
64
+ host: Annotated[Optional[str], typer.Option(help="QubeCore 서버 주소 (host:port)")] = None,
65
+ ver: Annotated[bool, typer.Option("-v", "--version", help="버전 출력", is_eager=True)] = False,
66
+ ) -> None:
67
+ if ver:
68
+ typer.echo(f"v{version('qubecli')}")
69
+ raise typer.Exit()
70
+ _state.address = _resolve_address(host)
71
+
72
+
73
+ @config_app.command("set")
74
+ def config_set(
75
+ key: Annotated[str, typer.Argument(help="설정 키")],
76
+ value: Annotated[str, typer.Argument(help="설정 값")],
77
+ ) -> None:
78
+ """설정값 저장 (예: qubecore config set host 192.168.1.100:50051)"""
79
+ if key not in ("host",):
80
+ typer.echo(f"알 수 없는 키: {key}. 사용 가능: host", err=True)
81
+ raise typer.Exit(1)
82
+ cfg = _load_config()
83
+ cfg[key] = value
84
+ _save_config(cfg)
85
+ typer.echo(f"✓ {key} = {value} 저장 완료 ({_CONFIG_PATH})")
86
+
87
+
88
+ @config_app.command("get")
89
+ def config_get(
90
+ key: Annotated[Optional[str], typer.Argument(help="설정 키 (생략 시 전체 출력)")] = None,
91
+ ) -> None:
92
+ """설정값 조회"""
93
+ cfg = _load_config()
94
+ if not cfg:
95
+ typer.echo("저장된 설정 없음")
96
+ return
97
+ if key:
98
+ typer.echo(f" {key} : {cfg.get(key, '(없음)')}")
99
+ else:
100
+ for k, v in cfg.items():
101
+ typer.echo(f" {k} : {v}")
102
+
103
+
104
+ @config_app.command("unset")
105
+ def config_unset(
106
+ key: Annotated[str, typer.Argument(help="삭제할 설정 키")],
107
+ ) -> None:
108
+ """설정값 삭제"""
109
+ cfg = _load_config()
110
+ if key not in cfg:
111
+ typer.echo(f" {key} : 설정된 값 없음")
112
+ return
113
+ del cfg[key]
114
+ _save_config(cfg)
115
+ typer.echo(f"✓ {key} 삭제 완료")
116
+
117
+
118
+ @app.command()
119
+ def submit(
120
+ backend: Annotated[str, typer.Option(help="백엔드 이름 (backend1, kreo.sc-20 등)")],
121
+ circuit: Annotated[str, typer.Option(help="회로 문자열 (QASM2/QASM3/JSON)")],
122
+ shots: Annotated[int, typer.Option(help="실행 횟수")],
123
+ priority: Annotated[Optional[int], typer.Option(help="우선순위 1~4 (기본값 3=NORMAL)")] = None,
124
+ optimization_level: Annotated[Optional[int], typer.Option(help="최적화 레벨 0~3")] = None,
125
+ ) -> None:
126
+ """게이트 회로 실행 작업 제출"""
127
+ with _get_client(_state.address) as client:
128
+ job_id = client.submit_gate_job(
129
+ backend_name=backend,
130
+ gate_circuits=[circuit.encode("raw_unicode_escape").decode("unicode_escape")],
131
+ shots=shots,
132
+ priority=priority,
133
+ optimization_level=optimization_level,
134
+ )
135
+ typer.echo("✓ 작업 제출 완료")
136
+ typer.echo(f" job_id : {job_id}")
137
+ typer.echo(f" priority : {_priority_label(priority or 3)}")
138
+ typer.echo(f" status : PENDING")
139
+
140
+
141
+ @app.command()
142
+ def status(
143
+ job_id: Annotated[str, typer.Option(help="작업 ID")],
144
+ ) -> None:
145
+ """작업 상태 조회"""
146
+ with _get_client(_state.address) as client:
147
+ job = client.get_job_status(job_id)
148
+ typer.echo(f" job_id : {job['job_id']}")
149
+ typer.echo(f" status : {job['status']}")
150
+ typer.echo(f" priority : {_priority_label(job['priority'])}")
151
+ typer.echo(f" submitted : {job['submitted_at']}")
152
+ typer.echo(f" started : {job['started_at'] or '(미시작)'}")
153
+ typer.echo(f" finished : {job['finished_at'] or '(미완료)'}")
154
+ if job["error"]:
155
+ typer.echo(f" error : {job['error']}")
156
+
157
+
158
+ @app.command()
159
+ def result(
160
+ job_id: Annotated[str, typer.Option(help="작업 ID")],
161
+ ) -> None:
162
+ """작업 결과 조회"""
163
+ with _get_client(_state.address) as client:
164
+ job = client.get_job_result(job_id)
165
+ typer.echo(f" job_id : {job['job_id']}")
166
+ typer.echo(f" status : {job['status']}")
167
+ for i, r in enumerate(job["results"]):
168
+ typer.echo(f" [회로 {i}] counts : {r['counts_json']}")
169
+
170
+
171
+ @app.command()
172
+ def cancel(
173
+ job_id: Annotated[str, typer.Option(help="작업 ID")],
174
+ ) -> None:
175
+ """작업 취소 (PENDING 상태일 때만 가능)"""
176
+ with _get_client(_state.address) as client:
177
+ message = client.cancel_job(job_id)
178
+ typer.echo(f"✓ {message}")
179
+
180
+
181
+ def main() -> None:
182
+ app()
183
+
184
+
185
+ if __name__ == "__main__":
186
+ main()
@@ -0,0 +1,135 @@
1
+ Metadata-Version: 2.4
2
+ Name: qubecli
3
+ Version: 1.0.0
4
+ Summary: QubeCore CLI — Command Line Interface for QubeCore
5
+ License: MIT License
6
+
7
+ Copyright (c) 2026 QubeCore Project
8
+
9
+ Permission is hereby granted, free of charge, to any person obtaining a copy
10
+ of this software and associated documentation files (the "Software"), to deal
11
+ in the Software without restriction, including without limitation the rights
12
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
+ copies of the Software, and to permit persons to whom the Software is
14
+ furnished to do so, subject to the following conditions:
15
+
16
+ The above copyright notice and this permission notice shall be included in all
17
+ copies or substantial portions of the Software.
18
+
19
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
+ SOFTWARE.
26
+ License-File: LICENSE
27
+ Requires-Python: >=3.11
28
+ Requires-Dist: qubecore-client>=1.0.10
29
+ Requires-Dist: typer>=0.12.0
30
+ Provides-Extra: dev
31
+ Requires-Dist: mypy>=1.8.0; extra == 'dev'
32
+ Requires-Dist: ruff>=0.1.0; extra == 'dev'
33
+ Description-Content-Type: text/markdown
34
+
35
+ # QubeCli
36
+
37
+ Command Line Interface for QubeCore — Quantum Computing Operating System.
38
+
39
+ ## Overview
40
+
41
+ QubeCli provides a terminal-based interface for submitting and managing
42
+ quantum jobs on QubeCore. Built on [qubecore-client](https://github.com/sdt-quantum/qubecore-client).
43
+
44
+ ## Requirements
45
+
46
+ - Python 3.11+
47
+ - QubeCore Server running and accessible
48
+
49
+ ## Installation
50
+
51
+ ```bash
52
+ pip install qubecli
53
+ ```
54
+
55
+ ## Quick Start
56
+
57
+ ```bash
58
+ # Set server address
59
+ qubecore config set host 192.168.1.100:50051
60
+
61
+ # Submit a job (use single quotes to avoid shell interpretation of \n)
62
+ qubecore submit --backend backend1 --circuit 'OPENQASM 2.0;\ninclude "qelib1.inc";\nqreg q[2];\ncreg c[2];\nh q[0];\ncx q[0],q[1];\nmeasure q -> c;' --shots 100
63
+
64
+ # Check status
65
+ qubecore status --job-id <job_id>
66
+
67
+ # Fetch result
68
+ qubecore result --job-id <job_id>
69
+
70
+ # Cancel job
71
+ qubecore cancel --job-id <job_id>
72
+ ```
73
+
74
+ ## Commands
75
+
76
+ ### Version
77
+
78
+ ```bash
79
+ qubecore -v
80
+ qubecore --version
81
+ ```
82
+
83
+ ### Server Configuration
84
+
85
+ ```bash
86
+ qubecore config set host 192.168.1.100:50051
87
+ qubecore config get
88
+ qubecore config get host
89
+ qubecore config unset host
90
+ ```
91
+
92
+ ### Job Commands
93
+
94
+ ```bash
95
+ # Submit
96
+ qubecore submit --backend <backend> --circuit <circuit_string> --shots <n>
97
+ qubecore submit --backend backend1 --circuit '...' --shots 100 --priority 2 --optimization-level 1
98
+
99
+ # Status
100
+ qubecore status --job-id <job_id>
101
+
102
+ # Result
103
+ qubecore result --job-id <job_id>
104
+
105
+ # Cancel (PENDING 상태일 때만 가능)
106
+ qubecore cancel --job-id <job_id>
107
+ ```
108
+
109
+ One-time server override:
110
+
111
+ ```bash
112
+ qubecore --host 10.0.0.1:50051 submit --backend backend1 --circuit "..." --shots 100
113
+ ```
114
+
115
+ ## Options
116
+
117
+ ### `submit`
118
+
119
+ | Option | Required | Description |
120
+ |--------|----------|-------------|
121
+ | `--backend` | Yes | 백엔드 이름 (`backend1`, `kreo.sc-20` 등) |
122
+ | `--circuit` | Yes | 회로 문자열 (QASM2/QASM3/JSON) |
123
+ | `--shots` | Yes | 실행 횟수 |
124
+ | `--priority` | No | 우선순위 1~4 (1=CRITICAL, 2=HIGH, 3=NORMAL, 4=LOW) |
125
+ | `--optimization-level` | No | 최적화 레벨 0~3 |
126
+
127
+ ## Compatibility
128
+
129
+ | qubecli | qubecore-client | qubecore |
130
+ |---------|----------------|----------|
131
+ | 1.x | 1.x | 1.x |
132
+
133
+ ## License
134
+
135
+ MIT
@@ -0,0 +1,7 @@
1
+ qubecli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ qubecli/main.py,sha256=MDjlRNGqEdq3Z6KciSsRGEnk_KgON3-1cv9RQELxYyM,5890
3
+ qubecli-1.0.0.dist-info/METADATA,sha256=7d1epRXAgkmPYFeiadQiUp8do33LTdcPxd4wFCF_8qo,3760
4
+ qubecli-1.0.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
5
+ qubecli-1.0.0.dist-info/entry_points.txt,sha256=wqFAyzHQSyGZAH-55bOWlBJxc0mxbPWRw6FxbkBcv1Q,47
6
+ qubecli-1.0.0.dist-info/licenses/LICENSE,sha256=MtfyWLHSlemM7mXY9Cx2xx97V_rzxlQhqXun0dXX2X8,1072
7
+ qubecli-1.0.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ qubecore = qubecli.main:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 QubeCore Project
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.