simpleArgParser 0.2.1__tar.gz → 0.2.3__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.
- simpleargparser-0.2.3/PKG-INFO +417 -0
- simpleargparser-0.2.3/README.md +406 -0
- simpleargparser-0.2.3/pyproject.toml +24 -0
- simpleargparser-0.2.3/simpleArgParser/__init__.py +12 -0
- {simpleargparser-0.2.1 → simpleargparser-0.2.3}/simpleArgParser/s_argparse.py +467 -40
- simpleargparser-0.2.3/simpleArgParser.egg-info/PKG-INFO +417 -0
- {simpleargparser-0.2.1 → simpleargparser-0.2.3}/simpleArgParser.egg-info/SOURCES.txt +3 -2
- {simpleargparser-0.2.1 → simpleargparser-0.2.3}/simpleArgParser.egg-info/top_level.txt +0 -1
- simpleargparser-0.2.3/tests/test_nested_defaults.py +275 -0
- simpleargparser-0.2.3/tests/test_subparser.py +639 -0
- simpleargparser-0.2.1/PKG-INFO +0 -51
- simpleargparser-0.2.1/README.md +0 -31
- simpleargparser-0.2.1/setup.py +0 -22
- simpleargparser-0.2.1/simpleArgParser/__init__.py +0 -10
- simpleargparser-0.2.1/simpleArgParser.egg-info/PKG-INFO +0 -51
- simpleargparser-0.2.1/tests/__init__.py +0 -1
- {simpleargparser-0.2.1 → simpleargparser-0.2.3}/setup.cfg +0 -0
- {simpleargparser-0.2.1 → simpleargparser-0.2.3}/simpleArgParser.egg-info/dependency_links.txt +0 -0
- {simpleargparser-0.2.1 → simpleargparser-0.2.3}/tests/test_alias_map.py +0 -0
- {simpleargparser-0.2.1 → simpleargparser-0.2.3}/tests/test_bool_converter.py +0 -0
- {simpleargparser-0.2.1 → simpleargparser-0.2.3}/tests/test_convert_type.py +0 -0
- {simpleargparser-0.2.1 → simpleargparser-0.2.3}/tests/test_convert_value.py +0 -0
- {simpleargparser-0.2.1 → simpleargparser-0.2.3}/tests/test_deep_merge.py +0 -0
- {simpleargparser-0.2.1 → simpleargparser-0.2.3}/tests/test_dict_utils.py +0 -0
- {simpleargparser-0.2.1 → simpleargparser-0.2.3}/tests/test_doc_help.py +0 -0
- {simpleargparser-0.2.1 → simpleargparser-0.2.3}/tests/test_enum.py +0 -0
- {simpleargparser-0.2.1 → simpleargparser-0.2.3}/tests/test_extract_field_comments.py +0 -0
- {simpleargparser-0.2.1 → simpleargparser-0.2.3}/tests/test_fill_defaults.py +0 -0
- {simpleargparser-0.2.1 → simpleargparser-0.2.3}/tests/test_from_dict.py +0 -0
- {simpleargparser-0.2.1 → simpleargparser-0.2.3}/tests/test_inheritance.py +0 -0
- {simpleargparser-0.2.1 → simpleargparser-0.2.3}/tests/test_nest_namespace.py +0 -0
- {simpleargparser-0.2.1 → simpleargparser-0.2.3}/tests/test_parse_args.py +0 -0
- {simpleargparser-0.2.1 → simpleargparser-0.2.3}/tests/test_pass_in_special_char.py +0 -0
- {simpleargparser-0.2.1 → simpleargparser-0.2.3}/tests/test_recursive_process.py +0 -0
- {simpleargparser-0.2.1 → simpleargparser-0.2.3}/tests/test_serialization.py +0 -0
- {simpleargparser-0.2.1 → simpleargparser-0.2.3}/tests/test_special_load_marker.py +0 -0
- {simpleargparser-0.2.1 → simpleargparser-0.2.3}/tests/test_to_json.py +0 -0
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: simpleArgParser
|
|
3
|
+
Version: 0.2.3
|
|
4
|
+
Summary: A simple typed argument parser using dataclasses and type hints. This project is largely generated by LLMs.
|
|
5
|
+
Author-email: raibows <raibows@hotmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/Raibows/SimpleArgParser
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Requires-Python: >=3.10
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
|
|
12
|
+
# SimpleArgParser
|
|
13
|
+
|
|
14
|
+
A simple typed argument parser for Python built on dataclasses. Define your config as a class, get CLI parsing, validation, and serialization.
|
|
15
|
+
|
|
16
|
+
This project is largely generated by LLMs.
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
pip install -U simpleArgParser
|
|
22
|
+
uv add simpleArgParser
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## Quick Start
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
from dataclasses import dataclass
|
|
29
|
+
from simpleArgParser import parse_args
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class Config:
|
|
33
|
+
name: str # required (no default)
|
|
34
|
+
epochs: int = 10 # optional with default
|
|
35
|
+
lr: float = 0.001
|
|
36
|
+
|
|
37
|
+
config = parse_args(Config)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
python main.py --name experiment1 --epochs 20
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Features
|
|
45
|
+
|
|
46
|
+
### Required and Optional Arguments
|
|
47
|
+
|
|
48
|
+
Fields without defaults are required. Fields with defaults are optional.
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
@dataclass
|
|
52
|
+
class Config:
|
|
53
|
+
required_field: str # must be provided
|
|
54
|
+
optional_field: int = 42 # has a default
|
|
55
|
+
optional_none: float | None = None # optional, defaults to None
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
python main.py --required_field hello
|
|
60
|
+
python main.py --required_field hello --optional_none 3.14
|
|
61
|
+
python main.py --required_field hello --optional_none none # explicitly set to None
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Bool Arguments
|
|
65
|
+
|
|
66
|
+
Accepts `true/false`, `yes/no`, `t/f`, `y/n`, `1/0` (case-insensitive).
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
@dataclass
|
|
70
|
+
class Config:
|
|
71
|
+
verbose: bool = False
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
python main.py --verbose true
|
|
76
|
+
python main.py --verbose yes
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Enum Arguments
|
|
80
|
+
|
|
81
|
+
Pass in the enum member **name**. Choices are displayed in `--help`.
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
import enum
|
|
85
|
+
|
|
86
|
+
class Mode(enum.Enum):
|
|
87
|
+
train = "train"
|
|
88
|
+
eval = "eval"
|
|
89
|
+
|
|
90
|
+
@dataclass
|
|
91
|
+
class Config:
|
|
92
|
+
mode: Mode = Mode.train
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
python main.py --mode eval
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### List Arguments
|
|
100
|
+
|
|
101
|
+
Comma-separated values. Supports `none` to pass `None`.
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
@dataclass
|
|
105
|
+
class Config:
|
|
106
|
+
devices: list[int] | None = None
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
python main.py --devices 0,1,2
|
|
111
|
+
python main.py --devices none
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Nested Dataclasses
|
|
115
|
+
|
|
116
|
+
Nest dataclasses as fields. Arguments use dot-separated names. Unique field names get short aliases automatically.
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
@dataclass
|
|
120
|
+
class OptimizerConfig:
|
|
121
|
+
lr: float = 0.001
|
|
122
|
+
weight_decay: float = 0.01
|
|
123
|
+
|
|
124
|
+
@dataclass
|
|
125
|
+
class Config:
|
|
126
|
+
name: str = "exp"
|
|
127
|
+
optimizer: OptimizerConfig = field(default_factory=OptimizerConfig)
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
# full path always works
|
|
132
|
+
python main.py --optimizer.lr 0.01
|
|
133
|
+
|
|
134
|
+
# short alias works when the name is unique across all fields
|
|
135
|
+
python main.py --lr 0.01 --weight_decay 0.05
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### Inheritance
|
|
139
|
+
|
|
140
|
+
Child dataclasses inherit parent fields. You can override defaults.
|
|
141
|
+
|
|
142
|
+
```python
|
|
143
|
+
@dataclass
|
|
144
|
+
class BaseConfig:
|
|
145
|
+
seed: int = 42
|
|
146
|
+
verbose: bool = False
|
|
147
|
+
|
|
148
|
+
@dataclass
|
|
149
|
+
class TrainConfig(BaseConfig):
|
|
150
|
+
lr: float = 0.001
|
|
151
|
+
verbose: bool = True # override parent default
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
python main.py --seed 123 --lr 0.01
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Comments as Help Text
|
|
159
|
+
|
|
160
|
+
Comments above or inline with fields are extracted and shown in `--help`.
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
@dataclass
|
|
164
|
+
class Config:
|
|
165
|
+
# Learning rate for the optimizer
|
|
166
|
+
lr: float = 0.001
|
|
167
|
+
epochs: int = 10 # number of training epochs
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
python main.py --help
|
|
172
|
+
# shows:
|
|
173
|
+
# --lr (type: float) (default: 0.001) Learning rate for the optimizer
|
|
174
|
+
# --epochs (type: int) (default: 10) number of training epochs
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### JSON Config Loading
|
|
178
|
+
|
|
179
|
+
Use `SpecialLoadMarker` to load defaults from a JSON file. Priority: command line > pass_in > JSON config > default values.
|
|
180
|
+
|
|
181
|
+
```python
|
|
182
|
+
from simpleArgParser import SpecialLoadMarker
|
|
183
|
+
|
|
184
|
+
@dataclass
|
|
185
|
+
class Config:
|
|
186
|
+
lr: float = 0.001
|
|
187
|
+
epochs: int = 10
|
|
188
|
+
load_from: str | None = SpecialLoadMarker()
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
```json
|
|
192
|
+
{"lr": 0.01, "epochs": 50}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
```bash
|
|
196
|
+
python main.py --load_from config.json # uses JSON values
|
|
197
|
+
python main.py --load_from config.json --lr 0.1 # CLI overrides JSON
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Partial Defaults for Nested Dataclasses
|
|
201
|
+
|
|
202
|
+
When a nested dataclass has required fields, you can't use it directly as a default (e.g. `field(default_factory=SamplingConfig)` fails because `SamplingConfig` has required fields). Use `partial_defaults()` to provide defaults for **some** fields while keeping others required from CLI.
|
|
203
|
+
|
|
204
|
+
```python
|
|
205
|
+
from simpleArgParser import parse_args, partial_defaults
|
|
206
|
+
|
|
207
|
+
@dataclass
|
|
208
|
+
class InnerConfig:
|
|
209
|
+
lr: float # required
|
|
210
|
+
weight_decay: float # required
|
|
211
|
+
warmup: int = 100
|
|
212
|
+
|
|
213
|
+
@dataclass
|
|
214
|
+
class Config:
|
|
215
|
+
# lr gets default 0.001, weight_decay stays required from CLI
|
|
216
|
+
optimizer: InnerConfig = partial_defaults(lr=0.001)
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
python main.py --optimizer.weight_decay 0.01
|
|
221
|
+
# optimizer.lr = 0.001 (from partial_defaults)
|
|
222
|
+
# optimizer.weight_decay = 0.01 (from CLI)
|
|
223
|
+
# optimizer.warmup = 100 (field default)
|
|
224
|
+
|
|
225
|
+
python main.py --optimizer.weight_decay 0.01 --optimizer.lr 0.1
|
|
226
|
+
# CLI overrides partial_defaults
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
#### Nested partial_defaults
|
|
230
|
+
|
|
231
|
+
`partial_defaults` can be nested to set defaults at multiple levels:
|
|
232
|
+
|
|
233
|
+
```python
|
|
234
|
+
@dataclass
|
|
235
|
+
class LeafConfig:
|
|
236
|
+
gen_n: int # required
|
|
237
|
+
cache: bool = True
|
|
238
|
+
|
|
239
|
+
@dataclass
|
|
240
|
+
class MiddleConfig:
|
|
241
|
+
max_tokens: int # required
|
|
242
|
+
temperature: float = 0.6
|
|
243
|
+
leaf: LeafConfig = partial_defaults(cache=False)
|
|
244
|
+
|
|
245
|
+
@dataclass
|
|
246
|
+
class OuterConfig:
|
|
247
|
+
mid: MiddleConfig = partial_defaults(
|
|
248
|
+
max_tokens=123,
|
|
249
|
+
leaf=partial_defaults(cache=False),
|
|
250
|
+
)
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
# Only gen_n is required — everything else has defaults
|
|
255
|
+
python main.py --mid.leaf.gen_n 10
|
|
256
|
+
# mid.max_tokens = 123
|
|
257
|
+
# mid.temperature = 0.6
|
|
258
|
+
# mid.leaf.gen_n = 10
|
|
259
|
+
# mid.leaf.cache = False
|
|
260
|
+
|
|
261
|
+
python main.py --mid.leaf.gen_n 10 --mid.leaf.cache true
|
|
262
|
+
# CLI overrides nested partial_defaults
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
In `--help`, fields with partial defaults show their default value, and fields without defaults show `(required)`:
|
|
266
|
+
|
|
267
|
+
```
|
|
268
|
+
--mid.leaf.gen_n (type: int) (required)
|
|
269
|
+
--mid.max_tokens (type: int) (default: 123)
|
|
270
|
+
--mid.leaf.cache (type: bool) (default: False)
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Pre/Post Processing
|
|
274
|
+
|
|
275
|
+
Define `pre_process()` and `post_process()` methods for validation or side effects. They are called recursively on all nested dataclasses (pre_process top-down, post_process bottom-up).
|
|
276
|
+
|
|
277
|
+
```python
|
|
278
|
+
@dataclass
|
|
279
|
+
class Config:
|
|
280
|
+
tp: int = 1
|
|
281
|
+
|
|
282
|
+
def pre_process(self):
|
|
283
|
+
print("validating...")
|
|
284
|
+
|
|
285
|
+
def post_process(self):
|
|
286
|
+
if self.tp < 1:
|
|
287
|
+
raise ValueError("tp must be >= 1")
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
### Serialization
|
|
291
|
+
|
|
292
|
+
Convert configs to JSON or dict. Enum values are serialized by name.
|
|
293
|
+
|
|
294
|
+
```python
|
|
295
|
+
from simpleArgParser import to_json, to_dict
|
|
296
|
+
|
|
297
|
+
config = parse_args(Config)
|
|
298
|
+
print(to_json(config)) # JSON string
|
|
299
|
+
print(to_dict(config)) # Python dict
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Programmatic Usage
|
|
303
|
+
|
|
304
|
+
Use `pass_in` to provide arguments from code. Use `disable_cmd=True` to ignore `sys.argv`. Very useful for debugging and testing.
|
|
305
|
+
|
|
306
|
+
```python
|
|
307
|
+
config = parse_args(Config, pass_in=["--lr", "0.01", "--epochs", "5"])
|
|
308
|
+
|
|
309
|
+
# ignore command line entirely
|
|
310
|
+
config = parse_args(Config, pass_in=["--lr", "0.01"], disable_cmd=True)
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### Subcommands (CLI Tools)
|
|
314
|
+
|
|
315
|
+
Build multi-command CLI tools with `parse_args_with_commands`. Define commands using enums for type-safe dispatching. Supports arbitrary nesting.
|
|
316
|
+
|
|
317
|
+
```python
|
|
318
|
+
import enum
|
|
319
|
+
from simpleArgParser import parse_args_with_commands
|
|
320
|
+
|
|
321
|
+
class Command(enum.Enum):
|
|
322
|
+
train = "train" # start training
|
|
323
|
+
eval = "eval" # run evaluation
|
|
324
|
+
|
|
325
|
+
@dataclass
|
|
326
|
+
class TrainConfig:
|
|
327
|
+
lr: float = 0.001
|
|
328
|
+
epochs: int = 10
|
|
329
|
+
|
|
330
|
+
@dataclass
|
|
331
|
+
class EvalConfig:
|
|
332
|
+
checkpoint: str # required
|
|
333
|
+
|
|
334
|
+
command, config = parse_args_with_commands(
|
|
335
|
+
commands={
|
|
336
|
+
Command.train: TrainConfig,
|
|
337
|
+
Command.eval: EvalConfig,
|
|
338
|
+
},
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
if command == (Command.train,):
|
|
342
|
+
print(f"Training with lr={config.lr}")
|
|
343
|
+
elif command == (Command.eval,):
|
|
344
|
+
print(f"Evaluating {config.checkpoint}")
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
```bash
|
|
348
|
+
mycli train --lr 0.01 --epochs 20
|
|
349
|
+
mycli eval --checkpoint best.pt
|
|
350
|
+
mycli --help
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
#### Nested Commands
|
|
354
|
+
|
|
355
|
+
Group commands into modules with nested dicts and separate enums per level. The returned `command` is a tuple of enum members.
|
|
356
|
+
|
|
357
|
+
```python
|
|
358
|
+
class Top(enum.Enum):
|
|
359
|
+
model = "model" # model operations
|
|
360
|
+
data = "data" # data operations
|
|
361
|
+
|
|
362
|
+
class ModelCmd(enum.Enum):
|
|
363
|
+
train = "train"
|
|
364
|
+
eval = "eval"
|
|
365
|
+
|
|
366
|
+
class DataCmd(enum.Enum):
|
|
367
|
+
process = "process"
|
|
368
|
+
|
|
369
|
+
command, config = parse_args_with_commands(
|
|
370
|
+
commands={
|
|
371
|
+
Top.model: {
|
|
372
|
+
ModelCmd.train: TrainConfig,
|
|
373
|
+
ModelCmd.eval: EvalConfig,
|
|
374
|
+
},
|
|
375
|
+
Top.data: {
|
|
376
|
+
DataCmd.process: ProcessConfig,
|
|
377
|
+
},
|
|
378
|
+
},
|
|
379
|
+
description="My ML CLI",
|
|
380
|
+
)
|
|
381
|
+
|
|
382
|
+
# command == (Top.model, ModelCmd.train)
|
|
383
|
+
if command[0] == Top.model:
|
|
384
|
+
if command[1] == ModelCmd.train:
|
|
385
|
+
...
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
```bash
|
|
389
|
+
mycli model train --lr 0.01
|
|
390
|
+
mycli data process --workers 8
|
|
391
|
+
mycli --help # shows command tree
|
|
392
|
+
mycli model --help # shows model sub-commands
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
#### Shared Config Across Commands
|
|
396
|
+
|
|
397
|
+
Embed a common config as a nested field. Short aliases are created automatically for unique field names.
|
|
398
|
+
|
|
399
|
+
```python
|
|
400
|
+
@dataclass
|
|
401
|
+
class CommonConfig:
|
|
402
|
+
verbose: bool = False
|
|
403
|
+
|
|
404
|
+
@dataclass
|
|
405
|
+
class TrainConfig:
|
|
406
|
+
lr: float = 0.001
|
|
407
|
+
common: CommonConfig = field(default_factory=CommonConfig)
|
|
408
|
+
```
|
|
409
|
+
|
|
410
|
+
```bash
|
|
411
|
+
mycli train --verbose true # alias for --common.verbose
|
|
412
|
+
mycli train --common.verbose true # full path always works
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
## Help Output Ordering
|
|
416
|
+
|
|
417
|
+
Arguments in `--help` are sorted by: required first, then by nesting depth (shallow first), then alphabetically.
|