11#!/usr/bin/env python3
22
3+
34from __future__ import annotations
45
56import sys
@@ -12,21 +13,42 @@ if sys.version_info[:2] < (3, 8):
1213import json
1314import os
1415
15- from argparse import ArgumentTypeError , ArgumentParser
16+ from argparse import ArgumentParser , ArgumentTypeError
1617from typing import Any , Callable
18+ from urllib .error import HTTPError , URLError
1719from urllib .parse import urlparse
18- from urllib .request import HTTPError , Request , urlopen
20+ from urllib .request import Request , urlopen
1921from urllib .response import addinfourl
20- from urllib .error import URLError
22+
23+ # --- Global Variables ---
2124
2225PROGRAM_NAME = os .path .basename (sys .argv [0 ])
26+ PROGRAM_DESCRIPTION = """
27+ ODC CLI Tool
28+
29+ This command-line tool allows system administrators to interact with the
30+ Open Data Capture (ODC) API. It provides commands for configuring the API
31+ endpoint, authenticating with credentials, and managing resources.
32+
33+ Requires:
34+ - Python 3.8+
35+
36+ Features:
37+ - Login with username and password
38+ - Store and retrieve configuration (e.g., base URL)
39+ """
40+
2341USER_CONFIG_FILEPATH = os .path .expanduser ('~/.odc-cli.json' )
2442
43+ config : Config # initialized in main
44+
2545
26- config : Config
46+ # --- Decorators ---
2747
2848
2949def require_url (fn : Callable [..., Any ]) -> Callable [..., Any ]:
50+ """Decorator to ensure a base URL is configured before executing the function"""
51+
3052 def wrapper (* args : Any , ** kwargs : Any ) -> None :
3153 if config .base_url is None :
3254 raise RuntimeError (f'URL must be defined (hint: use { PROGRAM_NAME } config set-url)' )
@@ -35,10 +57,13 @@ def require_url(fn: Callable[..., Any]) -> Callable[..., Any]:
3557 return wrapper
3658
3759
60+ # --- Utilities ---
61+
62+
3863class ArgumentTypes :
3964 @staticmethod
40- def valid_url_or_null (url : str ) -> str | None :
41- if url == 'null ' :
65+ def valid_url_or_none (url : str ) -> str | None :
66+ if url == 'None ' :
4267 return None
4368 parsed = urlparse (url )
4469 if not all ([parsed .scheme , parsed .netloc ]):
@@ -47,6 +72,8 @@ class ArgumentTypes:
4772
4873
4974class Config :
75+ """Manages user configuration stored in a JSON file"""
76+
5077 _dict : dict [str , Any ]
5178 _filepath = os .path .expanduser ('~/.odc-cli.json' )
5279
@@ -79,6 +106,8 @@ class Config:
79106
80107
81108class HttpResponse :
109+ """Represents an HTTP response with JSON data and status"""
110+
82111 def __init__ (self , data : Any , status : int | None ):
83112 self .data = data
84113 self .status = status
@@ -102,6 +131,8 @@ class HttpResponse:
102131
103132
104133class HttpClient :
134+ """Simplified HTTP client for making requests"""
135+
105136 @classmethod
106137 def post (cls , url : str , data : Any ) -> HttpResponse :
107138 headers : dict [str , str ] = {'Content-Type' : 'application/json' }
@@ -143,6 +174,9 @@ class HttpClient:
143174 return HttpResponse (data = data , status = response .getcode ())
144175
145176
177+ # --- Command Handlers ---
178+
179+
146180class AuthCommands :
147181 @require_url
148182 @staticmethod
@@ -158,19 +192,22 @@ class AuthCommands:
158192class ConfigCommands :
159193 @staticmethod
160194 def get_url () -> None :
161- print (config .base_url or 'null' )
195+ print (config .base_url )
162196
163197 @staticmethod
164- def set_url (url : str ) -> None :
198+ def set_url (url : str | None ) -> None :
165199 config .base_url = url
166200 config .write ()
167201
168202
203+ # --- Main CLI Entrypoint ---
204+
205+
169206def main () -> None :
170207 global config
171208 config = Config ()
172209
173- parser = ArgumentParser (prog = PROGRAM_NAME )
210+ parser = ArgumentParser (prog = PROGRAM_NAME , description = PROGRAM_DESCRIPTION )
174211 subparsers = parser .add_subparsers (help = 'subcommand help' , required = True )
175212
176213 ## AUTH
@@ -191,7 +228,7 @@ def main() -> None:
191228 get_config_url_parser .set_defaults (fn = ConfigCommands .get_url )
192229
193230 set_config_parser = config_subparsers .add_parser ('set-url' )
194- set_config_parser .add_argument ('url' , type = ArgumentTypes .valid_url_or_null )
231+ set_config_parser .add_argument ('url' , type = ArgumentTypes .valid_url_or_none )
195232 set_config_parser .set_defaults (fn = ConfigCommands .set_url )
196233
197234 kwargs = vars (parser .parse_args ())
0 commit comments