44"""
55import argparse
66import pathlib
7- from typing import Literal , NamedTuple , Union
7+ import re
8+ from typing import Literal , NamedTuple , Optional , Union
89
9- from httpx import URL
10+ from httpx import URL , Headers
1011
1112from csv2http ._version import __version__
1213
1314SUPPORTED_METHODS = ["POST" , "PATCH" , "PUT" ]
14-
1515CONCURRENCY_DEFAULT = 25
16+ TIMEOUT_DEFAULT = 5
17+
18+ _SPLIT_REGEX = r"[:=]"
19+
20+
21+ def _get_input (prompt : str ):
22+ return input (prompt )
1623
1724
1825def _normalize_url (value : Union [str , URL ]) -> URL :
@@ -23,20 +30,22 @@ def _normalize_url(value: Union[str, URL]) -> URL:
2330 return url
2431
2532
26- class Args (NamedTuple ):
27- """Expected user Args."""
33+ def _resolve_auth (value : str ) -> Union [tuple [str , str ], tuple [None , None ]]:
34+ """
35+ Parse username & password. Prompt for password if not provided.
36+ """
37+ username , * extras = re .split (_SPLIT_REGEX , value , maxsplit = 1 )
38+ password = " " .join (extras ) if extras else _get_input ("password:" )
39+ return username , password
2840
29- file : pathlib .Path
30- url : Union [URL , str ]
31- concurrency : int
32- method : Literal ["POST" , "PATCH" , "PUT" ]
33- form_data : bool = False
34- save_log : bool = True
35- # verbose: bool = True
3641
42+ def _parse_header (value : str ) -> tuple [str , str ]:
43+ """Splits string on `:` or `=` and returns a tuple of key, value."""
44+ key , value = re .split (_SPLIT_REGEX , value , maxsplit = 1 )
45+ return key , value
3746
38- def get_args () -> Args :
39- """Get user args from the command line."""
47+
48+ def _bootstrap_parser () -> argparse . ArgumentParser :
4049 parser = argparse .ArgumentParser (
4150 description = f"HTTP request for every row of a CSV file - v{ __version__ } "
4251 )
@@ -59,6 +68,16 @@ def get_args() -> Args:
5968 default = "POST" ,
6069 choices = SUPPORTED_METHODS ,
6170 )
71+ parser .add_argument (
72+ "-a" ,
73+ "--auth" ,
74+ help = "Basic Authentication enter <USERNAME>:<PASSWORD>."
75+ " If password is blank you will be prompted for input" ,
76+ type = _resolve_auth ,
77+ )
78+ parser .add_argument (
79+ "-H" , "--header" , help = "Header `key:value` pairs" , nargs = "*" , type = _parse_header
80+ )
6281 parser .add_argument (
6382 "-d" ,
6483 "--form-data" ,
@@ -71,18 +90,53 @@ def get_args() -> Args:
7190 help = "Do not save results to log file (default: false)" ,
7291 action = "store_true" ,
7392 )
93+ parser .add_argument (
94+ "-t" ,
95+ "--timeout" ,
96+ help = f"Connection timeout of each request in seconds (default: { TIMEOUT_DEFAULT } )" ,
97+ default = TIMEOUT_DEFAULT ,
98+ type = int ,
99+ )
74100 # parser.add_argument(
75- # "-v", "--verbose", help="verbose stdout logging", default=False, type=bool
101+ # "-v",
102+ # "--verbose",
103+ # help="verbose stdout logging",
104+ # action="store_true",
76105 # )
106+ return parser
107+
108+
109+ class Args (NamedTuple ):
110+ """Expected user Args."""
77111
78- args = parser .parse_args ()
112+ file : pathlib .Path
113+ url : Union [URL , str ]
114+ concurrency : int
115+ method : Literal ["POST" , "PATCH" , "PUT" ]
116+ auth : Optional [tuple [str , str ]] = None
117+ headers : Optional [Headers ] = None
118+ form_data : bool = False
119+ save_log : bool = True
120+ timeout : int = TIMEOUT_DEFAULT
121+ # verbose: bool = False
122+
123+
124+ _PARSER = _bootstrap_parser ()
125+
126+
127+ def get_args () -> Args :
128+ """Get user args from the command line."""
129+ args = _PARSER .parse_args ()
79130 return Args (
80131 args .file ,
81132 args .url ,
82133 args .concurrency ,
83134 args .method ,
135+ args .auth ,
136+ Headers (args .header ),
84137 args .form_data ,
85- not args .no_save
138+ not args .no_save ,
139+ args .timeout ,
86140 # args.verbose,
87141 )
88142
0 commit comments