cmdargparse 0.1.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.
cmdargparse/field.py ADDED
@@ -0,0 +1,778 @@
1
+ import argparse
2
+ import dataclasses
3
+ from typing import (
4
+ TYPE_CHECKING,
5
+ Annotated,
6
+ Any,
7
+ Callable,
8
+ Collection,
9
+ Literal,
10
+ Mapping,
11
+ NamedTuple,
12
+ Never,
13
+ Sequence,
14
+ Tuple,
15
+ Type,
16
+ TypeAlias,
17
+ TypedDict,
18
+ TypeVar,
19
+ Union,
20
+ Unpack,
21
+ overload,
22
+ )
23
+
24
+ from .unset import Unset, isunset, on_unset
25
+
26
+
27
+ # NOTE Due to the complexity of the positional-only arguments of some overloads,
28
+ # we disable the overload concistency check reporting.
29
+ # pyright: reportInconsistentOverload=false
30
+
31
+ # NOTE Some overloads are wrongfully (in my opinion) recognized as overlapping.
32
+ # It most likely is caused by the unpacked typed dict used for additional
33
+ # keyword arguments.
34
+ # For now we just disable the overlapping overload reporting.
35
+ # pyright: reportOverlappingOverload=false
36
+
37
+
38
+ # ################################ TYPING ######################################
39
+
40
+
41
+ T = TypeVar("T")
42
+
43
+ TONCE: TypeAlias = Any
44
+
45
+
46
+ _FieldType: TypeAlias = Annotated[Any, dataclasses.Field]
47
+
48
+
49
+ _DeclFormSpecifier: TypeAlias = Literal["--", "-"]
50
+
51
+
52
+ # ################################ TYPES #######################################
53
+
54
+
55
+ class _FieldDecls(NamedTuple):
56
+
57
+ form: _DeclFormSpecifier | None
58
+ """Specifies the default form of the ... declaration."""
59
+
60
+ decl: Annotated[
61
+ str | None,
62
+ Annotated[None, "[derived]"],
63
+ Annotated[None, "[augmented]"],
64
+ Annotated[str, "[explicit]"],
65
+ Annotated[str, "[keyword]"],
66
+ ]
67
+ """Specifies the primary ... declaration."""
68
+
69
+ altdecl: Annotated[
70
+ str | None,
71
+ Annotated[None, "[derived]"],
72
+ Annotated[str, "[augmented]"],
73
+ Annotated[str | None, "[explicit]"],
74
+ Annotated[str | None, "[keyword]"],
75
+ ]
76
+ """Specifies the alternative ... declaration."""
77
+
78
+ more_decls: Sequence[str] | None
79
+ """Specifies additional ... declarations."""
80
+
81
+
82
+ class _DeclsCompatibleArgs(TypedDict, total=False):
83
+
84
+ form: _DeclFormSpecifier
85
+ """Specifies the default form of the ... declaration."""
86
+ decls: Tuple[str, str] | str
87
+ """Explicitly specifies all ... declarations."""
88
+ more_decls: Sequence[str] | str
89
+ """Specifies additional ... declarations."""
90
+
91
+
92
+ # ################################ METACLASS ###################################
93
+
94
+
95
+ class _cmdfield(type):
96
+
97
+ # Declarations Specification Formats
98
+ # - derived (default)
99
+ # The declaration name will be automatically derived from the field name.
100
+ # - augmented
101
+ # The specified declaration will replace the automatically derived
102
+ # declaration if both have the same form. Otherwise it will be added as
103
+ # an additional declaration.
104
+ # - explicit
105
+ # The specified declaration(s) (positional arguments) will replace the
106
+ # automatically derived declaration.
107
+ # - keyword
108
+ # The specified declaration(s) (`decls` keyword argument) will replace the
109
+ # automatically derived declaration.
110
+
111
+ # ################## ARGUMENT ##########################
112
+
113
+ class _ArgumentArgs(TypedDict, total=False):
114
+
115
+ type: Annotated[Any, Callable[[str], Annotated[Any, "FieldType"]]]
116
+
117
+ help: str
118
+ helpvar: str
119
+
120
+ @overload
121
+ # overload @ argument (required)
122
+ def argument(
123
+ cls,
124
+ /,
125
+ # *,
126
+ **args: Unpack[_ArgumentArgs],
127
+ ) -> _FieldType: ...
128
+
129
+ @overload
130
+ # overload @ argument with default
131
+ def argument(
132
+ cls,
133
+ /,
134
+ *,
135
+ default: TONCE,
136
+ **args: Unpack[_ArgumentArgs],
137
+ ) -> _FieldType: ...
138
+
139
+ @overload
140
+ # overload @ choice argument (required)
141
+ def argument(
142
+ cls,
143
+ /,
144
+ choices: Collection[T],
145
+ choicesmap: Mapping[T, T] | None = ...,
146
+ # *,
147
+ **args: Unpack[_ArgumentArgs],
148
+ ) -> _FieldType: ...
149
+
150
+ @overload
151
+ # overload @ choice argument with default
152
+ def argument(
153
+ cls,
154
+ /,
155
+ choices: Collection[T],
156
+ choicesmap: Mapping[T, T] | None = ...,
157
+ *,
158
+ default: TONCE,
159
+ **args: Unpack[_ArgumentArgs],
160
+ ) -> _FieldType: ...
161
+
162
+ def argument(
163
+ cls,
164
+ /,
165
+ choices: Collection[T] | None = None,
166
+ choicesmap: Mapping[T, T] | None = None,
167
+ *,
168
+ default: TONCE | Unset = ~Unset,
169
+ **args: Unpack[_ArgumentArgs],
170
+ ) -> _FieldType:
171
+ """
172
+ Defines a command-line argument. (positional)
173
+
174
+ :param choices:
175
+ The restricted set of values allowed for the argument.
176
+ :param choicesmap:
177
+ Additional values allowed for the argument that each map to the
178
+ specified value in the restricted set of values.
179
+ :param default:
180
+ The arguments default value if not specified.
181
+ (Providing a default value makes the argument optional.)
182
+
183
+ :param type:
184
+ Explicitly specifies the `type` to be used by `argparse`.
185
+ <br/> https://docs.python.org/3/library/argparse.html#type
186
+
187
+ :param help:
188
+ Brief description of the argument.
189
+ :param helpvar:
190
+ Reference name of the argument value in the help message.
191
+
192
+ """
193
+ return _argparse(
194
+ None,
195
+ nargs=("?" if not isunset(default) else None),
196
+ choices=choices,
197
+ choicesmap=choicesmap,
198
+ default=on_unset(default, None),
199
+ type=args.get("type", None),
200
+ help=args.get("help", None),
201
+ metavar=args.get("helpvar", None),
202
+ )
203
+
204
+ # ################## OPTION ############################
205
+
206
+ class _OptionArgs(TypedDict, total=False):
207
+
208
+ form: _DeclFormSpecifier
209
+ decls: Tuple[str, str] | str
210
+ more_decls: Sequence[str] | str
211
+
212
+ type: Annotated[Any, Callable[[str], Annotated[Any, "FieldType"]]]
213
+
214
+ required: bool
215
+
216
+ help: str
217
+ helpvar: str
218
+
219
+ @overload
220
+ # overload @ option [derived]
221
+ def option(
222
+ cls,
223
+ /,
224
+ # *,
225
+ **args: Unpack[_OptionArgs],
226
+ ) -> _FieldType: ...
227
+
228
+ @overload
229
+ # overload @ option [augmented]
230
+ def option(
231
+ cls,
232
+ decl: str,
233
+ /,
234
+ # *,
235
+ **args: Unpack[_OptionArgs],
236
+ ) -> _FieldType: ...
237
+
238
+ @overload
239
+ # overload @ option [explicit]
240
+ def option(
241
+ cls,
242
+ decl: str,
243
+ altdecl: str,
244
+ /,
245
+ # *,
246
+ **args: Unpack[_OptionArgs],
247
+ ) -> _FieldType: ...
248
+
249
+ @overload
250
+ # overload @ option with default [derived]
251
+ def option(
252
+ cls,
253
+ /,
254
+ *,
255
+ default: TONCE,
256
+ **args: Unpack[_OptionArgs],
257
+ ) -> _FieldType: ...
258
+
259
+ @overload
260
+ # overload @ option with default [augmented]
261
+ def option(
262
+ cls,
263
+ decl: str,
264
+ /,
265
+ *,
266
+ default: TONCE,
267
+ **args: Unpack[_OptionArgs],
268
+ ) -> _FieldType: ...
269
+
270
+ @overload
271
+ # overload @ option with default [explicit]
272
+ def option(
273
+ cls,
274
+ decl: str,
275
+ altdecl: str,
276
+ /,
277
+ *,
278
+ default: TONCE,
279
+ **args: Unpack[_OptionArgs],
280
+ ) -> _FieldType: ...
281
+
282
+ @overload
283
+ # overload @ choice option [derived]
284
+ def option(
285
+ cls,
286
+ /,
287
+ choices: Collection[T],
288
+ choicesmap: Mapping[T, T] | None = ...,
289
+ # *,
290
+ **args: Unpack[_OptionArgs],
291
+ ) -> _FieldType: ...
292
+
293
+ @overload
294
+ # overload @ choice option [augmented]
295
+ def option(
296
+ cls,
297
+ decl: str,
298
+ /,
299
+ choices: Collection[T],
300
+ choicesmap: Mapping[T, T] | None = ...,
301
+ # *,
302
+ **args: Unpack[_OptionArgs],
303
+ ) -> _FieldType: ...
304
+
305
+ @overload
306
+ # overload @ choice option [explicit]
307
+ def option(
308
+ cls,
309
+ decl: str,
310
+ altdecl: str,
311
+ /,
312
+ choices: Collection[T],
313
+ choicesmap: Mapping[T, T] | None = ...,
314
+ # *,
315
+ **args: Unpack[_OptionArgs],
316
+ ) -> _FieldType: ...
317
+
318
+ @overload
319
+ # overload @ choice option with default [derived]
320
+ def option(
321
+ cls,
322
+ /,
323
+ choices: Collection[T],
324
+ choicesmap: Mapping[T, T] | None = ...,
325
+ *,
326
+ default: TONCE,
327
+ **args: Unpack[_OptionArgs],
328
+ ) -> _FieldType: ...
329
+
330
+ @overload
331
+ # overload @ choice option with default [augmented]
332
+ def option(
333
+ cls,
334
+ decl: str,
335
+ /,
336
+ choices: Collection[T],
337
+ choicesmap: Mapping[T, T] | None = ...,
338
+ *,
339
+ default: TONCE,
340
+ **args: Unpack[_OptionArgs],
341
+ ) -> _FieldType: ...
342
+
343
+ @overload
344
+ # overload @ choice option with default [explicit]
345
+ def option(
346
+ cls,
347
+ decl: str,
348
+ altdecl: str,
349
+ /,
350
+ choices: Collection[T],
351
+ choicesmap: Mapping[T, T] | None = ...,
352
+ *,
353
+ default: TONCE,
354
+ **args: Unpack[_OptionArgs],
355
+ ) -> _FieldType: ...
356
+
357
+ def option(
358
+ cls,
359
+ decl: str | None = None,
360
+ altdecl: str | None = None,
361
+ /,
362
+ choices: Collection[T] | None = None,
363
+ choicesmap: Mapping[T, T] | None = None,
364
+ *,
365
+ default: TONCE | Unset = ~Unset,
366
+ **args: Unpack[_OptionArgs],
367
+ ) -> _FieldType:
368
+ """
369
+ Defines a command-line option. (non-positional, value-bound)
370
+
371
+ :param decl:
372
+ Specifies the primary option declaration.
373
+ :param altdecl:
374
+ Specifies the alternative option declaration.
375
+
376
+ :param choices:
377
+ The restricted set of values allowed for the option.
378
+ :param choicesmap:
379
+ Additional values allowed for the option that each map to the
380
+ specified value in the restricted set of values.
381
+ :param default:
382
+ The options default value if not specified.
383
+
384
+ :param form:
385
+ Specifies the default form of the option declaration.
386
+ :param decls:
387
+ Explicitly specifies all option declarations.
388
+ :param more_decls:
389
+ Specifies additional option declarations.
390
+
391
+ :param type:
392
+ Explicitly specifies the `type` to be used by `argparse`.
393
+ <br/> https://docs.python.org/3/library/argparse.html#type
394
+
395
+ :param required:
396
+ Marks the option as required.
397
+
398
+ :param help:
399
+ Brief description of the option.
400
+ :param helpvar:
401
+ Reference name of the option value in the help message.
402
+
403
+ """
404
+ if decl is None:
405
+ decl, altdecl, choices = (
406
+ # 0 arguments
407
+ (None, None, None)
408
+ ) # type: ignore
409
+ elif altdecl is None:
410
+ decl, altdecl, choices = (
411
+ # 1 argument
412
+ (decl, None, None)
413
+ if isinstance(decl, str)
414
+ else (None, None, decl)
415
+ ) # type: ignore
416
+ elif choices is None:
417
+ decl, altdecl, choices = (
418
+ # 2 arguments
419
+ (decl, altdecl, None)
420
+ if isinstance(decl, str)
421
+ else (decl, None, altdecl)
422
+ ) # type: ignore
423
+ else:
424
+ decl, altdecl, choices = (
425
+ # 3 arguments
426
+ (decl, altdecl, choices)
427
+ ) # type: ignore
428
+ return _argparse(
429
+ _decls(decl, altdecl, args),
430
+ nargs=("?" if not isunset(default) else None),
431
+ choices=choices,
432
+ choicesmap=choicesmap,
433
+ default=on_unset(default, None),
434
+ required=args.get("required", False),
435
+ type=args.get("type", None),
436
+ help=args.get("help", None),
437
+ metavar=args.get("helpvar", None),
438
+ )
439
+
440
+ # ################## FLAG ##############################
441
+
442
+ class _FlagArgs(TypedDict, total=False):
443
+
444
+ form: _DeclFormSpecifier
445
+ decls: Tuple[str, str] | str
446
+ more_decls: Sequence[str] | str
447
+
448
+ type: Annotated[Any, Callable[[str], Annotated[Any, "FieldType"]]]
449
+
450
+ help: str
451
+
452
+ @overload
453
+ # overload @ flag [derived]
454
+ def flag(
455
+ cls,
456
+ /,
457
+ # *,
458
+ **args: Unpack[_FlagArgs],
459
+ ) -> _FieldType: ...
460
+
461
+ @overload
462
+ # overload @ flag [augmented]
463
+ def flag(
464
+ cls,
465
+ decl: str,
466
+ /,
467
+ # *,
468
+ **args: Unpack[_FlagArgs],
469
+ ) -> _FieldType: ...
470
+
471
+ @overload
472
+ # overload @ flag [explicit]
473
+ def flag(
474
+ cls,
475
+ decl: str,
476
+ altdecl: str,
477
+ /,
478
+ # *,
479
+ **args: Unpack[_FlagArgs],
480
+ ) -> _FieldType: ...
481
+
482
+ @overload
483
+ # overload @ inverse flag [derived]
484
+ def flag(
485
+ cls,
486
+ /,
487
+ *,
488
+ invert: bool,
489
+ **args: Unpack[_FlagArgs],
490
+ ) -> _FieldType: ...
491
+
492
+ @overload
493
+ # overload @ inverse flag [augmented]
494
+ def flag(
495
+ cls,
496
+ decl: str,
497
+ /,
498
+ *,
499
+ invert: bool,
500
+ **args: Unpack[_FlagArgs],
501
+ ) -> _FieldType: ...
502
+
503
+ @overload
504
+ # overload @ inverse flag [explicit]
505
+ def flag(
506
+ cls,
507
+ decl: str,
508
+ altdecl: str,
509
+ /,
510
+ *,
511
+ invert: bool,
512
+ **args: Unpack[_FlagArgs],
513
+ ) -> _FieldType: ...
514
+
515
+ @overload
516
+ # overload @ constant-valued flag [derived]
517
+ def flag(
518
+ cls,
519
+ /,
520
+ *,
521
+ const: TONCE,
522
+ **args: Unpack[_FlagArgs],
523
+ ) -> _FieldType: ...
524
+
525
+ @overload
526
+ # overload @ constant-valued flag [augmented]
527
+ def flag(
528
+ cls,
529
+ decl: str,
530
+ /,
531
+ *,
532
+ const: TONCE,
533
+ **args: Unpack[_FlagArgs],
534
+ ) -> _FieldType: ...
535
+
536
+ @overload
537
+ # overload @ constant-valued flag [explicit]
538
+ def flag(
539
+ cls,
540
+ decl: str,
541
+ altdecl: str,
542
+ /,
543
+ *,
544
+ const: TONCE,
545
+ **args: Unpack[_FlagArgs],
546
+ ) -> _FieldType: ...
547
+
548
+ def flag(
549
+ cls,
550
+ decl: str | None = None,
551
+ altdecl: str | None = None,
552
+ /,
553
+ *,
554
+ invert: bool = False,
555
+ const: TONCE | Unset = ~Unset,
556
+ **args: Unpack[_FlagArgs],
557
+ ) -> _FieldType:
558
+ """
559
+ Defines a command-line flag. (non-positional, non-value)
560
+
561
+ :param decl:
562
+ Specifies the primary flag declaration.
563
+ :param altdecl:
564
+ Specifies the alternative flag declaration.
565
+
566
+ :param invert:
567
+ Sets the value `False` instead of `True` if the flag is specified.
568
+ :param const:
569
+ The value to set if the flag is specified.
570
+
571
+ :param form:
572
+ Specifies the default form of the flag declaration.
573
+ :param decls:
574
+ Explicitly specifies all flag declarations.
575
+ :param more_decls:
576
+ Specifies additional flag declarations.
577
+
578
+ :param type:
579
+ Explicitly specifies the `type` to be used by `argparse`.
580
+ <br/> https://docs.python.org/3/library/argparse.html#type
581
+
582
+ :param help:
583
+ Brief description of the flag.
584
+
585
+ """
586
+ return _argparse(
587
+ _decls(decl, altdecl, args),
588
+ action=(
589
+ ("store_false" if invert else "store_true")
590
+ if isunset(const)
591
+ else "store_const"
592
+ ),
593
+ const=on_unset(const, None),
594
+ type=args.get("type", None),
595
+ help=args.get("help", None),
596
+ )
597
+
598
+
599
+ # ################################ CLASS #######################################
600
+
601
+
602
+ class cmdfield(metaclass=_cmdfield):
603
+
604
+ pass
605
+
606
+
607
+ # ################################ ARGPARSE ####################################
608
+ # https://docs.python.org/3/library/argparse.html#the-add-argument-method
609
+
610
+ if TYPE_CHECKING:
611
+
612
+ def _argparse(
613
+ decls: _FieldDecls | None,
614
+ /,
615
+ *,
616
+ # ####### argparse #############
617
+ action: Union[
618
+ Type[argparse.Action],
619
+ Literal[
620
+ "store",
621
+ "store_const",
622
+ "store_true",
623
+ "store_false",
624
+ "append",
625
+ "append_const",
626
+ "count",
627
+ "help",
628
+ "version",
629
+ "extend",
630
+ ],
631
+ Annotated[Never, "<unspecified>"],
632
+ ] = ...,
633
+ nargs: Union[
634
+ Annotated[int, "N"],
635
+ Literal["?", "*", "+"],
636
+ Annotated[None, "<unspecified>"],
637
+ ] = ...,
638
+ const: Union[
639
+ Annotated[Any, TONCE],
640
+ Annotated[None, "<unspecified>"],
641
+ ] = ...,
642
+ default: Union[
643
+ Annotated[Any, TONCE],
644
+ Annotated[None, "<unspecified>"],
645
+ ] = ...,
646
+ type: Union[
647
+ Annotated[Any, Callable[[str], Annotated[Any, "FieldType"]]],
648
+ Annotated[None, "<unspecified>"],
649
+ ] = ...,
650
+ choices: Union[
651
+ Collection[Annotated[Any, T]],
652
+ Annotated[None, "<unspecified>"],
653
+ ] = ...,
654
+ required: Union[
655
+ bool,
656
+ Annotated[Never, "<unspecified>"],
657
+ ] = ...,
658
+ help: Union[
659
+ str,
660
+ Annotated[None, "<unspecified>"],
661
+ ] = ...,
662
+ metavar: Union[
663
+ Tuple[str, ...] | str,
664
+ Annotated[None, "<unspecified>"],
665
+ ] = ...,
666
+ dest: Annotated[
667
+ Never, "Specifying 'dest' directly is not supported."
668
+ ] = ...,
669
+ deprecated: Union[
670
+ bool,
671
+ Annotated[Never, "<unspecified>"],
672
+ ] = ...,
673
+ version: Union[
674
+ str,
675
+ Annotated[Never, "<unspecified>"],
676
+ ] = ...,
677
+ # ####### custom ###############
678
+ choicesmap: Union[
679
+ Mapping[Annotated[Any, T], Annotated[Any, T, "choices"]],
680
+ Annotated[None, "<unspecified>"],
681
+ ] = ...,
682
+ ) -> _FieldType: ...
683
+
684
+ else:
685
+
686
+ def _argparse(
687
+ decls: Any,
688
+ **args: Any,
689
+ ) -> _FieldType:
690
+ return dataclasses.field(
691
+ init=False,
692
+ repr=False,
693
+ compare=False,
694
+ metadata={
695
+ "decls": decls,
696
+ "args": args,
697
+ },
698
+ )
699
+
700
+
701
+ def _decls(
702
+ decl: str | None,
703
+ altdecl: str | None,
704
+ /,
705
+ args: _DeclsCompatibleArgs,
706
+ ) -> _FieldDecls:
707
+ (form, decls, more_decls) = (
708
+ args.get("form", None),
709
+ args.get("decls", None),
710
+ args.get("more_decls", None),
711
+ )
712
+
713
+ if isinstance(more_decls, str):
714
+ more_decls = (more_decls,)
715
+
716
+ if decls is not None:
717
+ # Declaration Specification Format: [keyword]
718
+
719
+ if decl is not None or altdecl is not None:
720
+ raise ValueError(
721
+ "Declarations cannot be specified using the positional "
722
+ "arguments if the 'decls' keyword argument is specified."
723
+ )
724
+
725
+ (_decl, _altdecl) = (
726
+ (decls, None)
727
+ if isinstance(decls, str)
728
+ else decls
729
+ # <format-break>
730
+ )
731
+ if (
732
+ (_decl and not _decl.startswith("-"))
733
+ or (_altdecl and not _altdecl.startswith("-"))
734
+ # <format-break>
735
+ ):
736
+ raise ValueError(
737
+ "All declarations must start with either '--' or '-'."
738
+ )
739
+
740
+ return _FieldDecls(None, _decl, _altdecl, more_decls)
741
+
742
+ elif decl is None and altdecl is None:
743
+ # Declaration Specification Format: [derived]
744
+
745
+ return _FieldDecls(form, None, None, more_decls)
746
+
747
+ elif decl is not None and altdecl is not None:
748
+ # Declaration Specification Format: [explicit]
749
+
750
+ if not decl.startswith("-") or not altdecl.startswith("-"):
751
+ raise ValueError(
752
+ "All declarations must start with either '--' or '-'."
753
+ )
754
+ elif (
755
+ (decl.startswith("--") and altdecl.startswith("--"))
756
+ or (not decl.startswith("--") and not altdecl.startswith("--"))
757
+ # <format-break>
758
+ ):
759
+ raise ValueError(
760
+ "Declarations specified using the positional arguments must "
761
+ "have different forms."
762
+ )
763
+
764
+ return _FieldDecls(form, decl, altdecl, more_decls)
765
+
766
+ else:
767
+ # Declaration Specification Format: [augmented]
768
+ assert decl is not None and altdecl is None, (
769
+ "The augmented declaration specification format should always have "
770
+ "'decl' specified and 'altdecl' unspecified."
771
+ )
772
+
773
+ if not decl.startswith("-"):
774
+ raise ValueError(
775
+ "All declarations must start with either '--' or '-'."
776
+ )
777
+
778
+ return _FieldDecls(form, None, decl, more_decls)