argman 0.2.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.
argman/__init__.py ADDED
@@ -0,0 +1 @@
1
+ from .argman import ArgMan, ArgParseError
argman/argman.py ADDED
@@ -0,0 +1,565 @@
1
+ from collections import OrderedDict
2
+ from dataclasses import dataclass
3
+ import sys
4
+
5
+
6
+ @dataclass
7
+ class _Arg:
8
+ short: str = None
9
+ long: str = None
10
+ type: type = None
11
+ item_type: type = str
12
+ default: int | float | str | list = None
13
+ desc: str = None
14
+
15
+
16
+ @dataclass
17
+ class _PosArg:
18
+ name: str
19
+ type: type = str
20
+ default: int | float | str = None
21
+ required: bool = False
22
+ parsed: bool = False
23
+ desc: str = None
24
+
25
+
26
+ class _ArgResult:
27
+ def __init__(self, aliases: dict[str, str]):
28
+ self.__dict__['_values'] = {}
29
+ self.__dict__['_aliases'] = aliases
30
+
31
+ def __getattr__(self, name):
32
+ key = self._aliases.get(name, name)
33
+ if key in self._values:
34
+ return self._values[key]
35
+ raise AttributeError(f"No such argument: {name}")
36
+
37
+ def __setattr__(self, name, value):
38
+ key = self._aliases.get(name, name)
39
+ self._values[key] = value
40
+
41
+ def __getitem__(self, key):
42
+ return getattr(self, key)
43
+
44
+ def __iter__(self):
45
+ return iter(self._values.items())
46
+
47
+ def __repr__(self):
48
+ args = ', '.join(f"{k}={v}" for k, v in self._values.items())
49
+ return f"<ArgResult {args}>"
50
+
51
+
52
+ class ArgParseError(Exception):
53
+ """Exception raised for errors in command-line argument parsing."""
54
+
55
+ def __init__(self, message: str):
56
+ self.message = message
57
+ super().__init__(message)
58
+
59
+
60
+ class ArgMan:
61
+ def __init__(self, prog=None, exit_on_err=True):
62
+ self.program = prog or sys.argv[0]
63
+ self.exit_on_err = exit_on_err
64
+ self.argv = sys.argv[1:]
65
+ self.argc = len(self.argv)
66
+ self.pos_only = False
67
+ self.args: dict[str, _Arg] = {}
68
+ self.pos_args: OrderedDict[str, _PosArg] = OrderedDict()
69
+ self.aliases: dict[str, str] = {}
70
+ self.result = _ArgResult(self.aliases)
71
+
72
+ def __set_arg(self, _type: type, short: str = None, long: str = None, default=None, desc=None, item_type=None):
73
+ """
74
+ Internal helper for registering an argument.
75
+ """
76
+ if short is None and long is None:
77
+ raise ValueError("At least one of 'short' or 'long' must be provided")
78
+ if short is not None and (not isinstance(short, str) or len(short) != 1):
79
+ raise ValueError("Short name must be a single character")
80
+ if long is not None and (not isinstance(long, str) or len(long) < 2):
81
+ raise ValueError("Long name must be at least 2 characters")
82
+ _long = long
83
+ if _long is not None:
84
+ _long = _long.replace('-', '_')
85
+ main_name = long or short
86
+ arg = _Arg(
87
+ short=short, long=_long, type=_type,
88
+ default=default, desc=desc, item_type=item_type
89
+ )
90
+ self.args[main_name] = arg
91
+ setattr(self.result, main_name, default)
92
+ if long:
93
+ self.aliases[long] = main_name
94
+ if short:
95
+ self.aliases[short] = main_name
96
+ return None
97
+
98
+ def _print_help(self):
99
+ NAME_MAX_LEN = 22
100
+
101
+ def get_arg_name(arg):
102
+ name = ''
103
+ if arg.short and arg.long:
104
+ name = f'-{arg.short}, --{arg.long}'
105
+ elif arg.short:
106
+ name = f'-{arg.short}'
107
+ elif arg.long:
108
+ name = f'--{arg.long}'
109
+ if arg.type:
110
+ if arg.type is list:
111
+ name += f' <{arg.type.__name__}[{arg.item_type.__name__}]>'
112
+ else:
113
+ name += f' <{arg.type.__name__}>'
114
+
115
+ return name
116
+
117
+ header = f"Usage: {self.program}"
118
+ opt_poses = []
119
+ req_poses = []
120
+ for arg in self.pos_args.values():
121
+ if arg.required:
122
+ req_poses.append(arg)
123
+ else:
124
+ opt_poses.append(arg)
125
+
126
+ if len(self.args) > 0:
127
+ header += ' [OPTIONS]'
128
+ if req_poses:
129
+ text = ' '
130
+ text += ' '.join([arg.name for arg in req_poses])
131
+ header += text
132
+ if opt_poses:
133
+ text = ''
134
+ for arg in opt_poses:
135
+ text += f' [{arg.name}]'
136
+ header += text
137
+ print(header)
138
+ if len(self.args) > 0:
139
+ print("\nOptions:")
140
+ for arg in self.args.values():
141
+ NAME_MAX_LEN = max(NAME_MAX_LEN, len(get_arg_name(arg)))
142
+ for arg in self.args.values():
143
+ arg_name = get_arg_name(arg)
144
+ print(f" {arg_name:<{NAME_MAX_LEN}} : {arg.desc.capitalize() if arg.desc else 'No description'}")
145
+ if len(req_poses) > 0 or len(opt_poses) > 0:
146
+ print("\nArguments:")
147
+ for arg in req_poses + opt_poses:
148
+ arg_name = f'{arg.name} <{arg.type.__name__}>'
149
+ text = f" {arg_name:<{NAME_MAX_LEN}} : {arg.desc.capitalize() if arg.desc else 'No description'}"
150
+ if arg.default is not None and not arg.required:
151
+ text += f' (optional, default: {arg.default})'
152
+ elif arg.default is not None:
153
+ text += f' [default: {arg.default}]'
154
+ elif not arg.required:
155
+ text += f' (optional)'
156
+ print(text)
157
+
158
+ def _print_err(self, message: str):
159
+ if self.exit_on_err:
160
+ print(message, file=sys.stderr)
161
+ self._print_help()
162
+ exit(1)
163
+ raise ArgParseError(message)
164
+
165
+ def arg_pos(self, name: str, *, required=True, default=None, _type=str, desc=None):
166
+ """
167
+ Define a positional argument.
168
+
169
+ Args:
170
+ name (str): Name of the argument variable.
171
+ required (bool, optional): Whether the argument must be provided. Defaults to True.
172
+ default (any, optional): Default value for the argument if not provided.
173
+ _type (type, optional): Type to which the argument value should be converted. Defaults to str.
174
+ desc (str, optional): Description for the argument, used in help messages.
175
+
176
+ Raises:
177
+ ValueError:
178
+ - If the argument is required but no default value is provided.
179
+
180
+ Examples:
181
+ >>> am = ArgMan()
182
+ >>> am.arg_pos('input_path', required=False, desc='Path to the input file')
183
+ >>> args = am.parse()
184
+ >>> print(args.input_path)
185
+ """
186
+ if default is not None and not isinstance(default, _type):
187
+ raise ValueError("Type of default value should be the same as defined type")
188
+ if required is True:
189
+ no_req = [name for name, arg in self.pos_args.items() if not arg.required]
190
+ if no_req:
191
+ raise ValueError(
192
+ "Required positional argument cannot be defined after an optional one. All required arguments must come first.")
193
+ arg = _PosArg(
194
+ name=name, type=_type,
195
+ default=default, desc=desc,
196
+ required=required
197
+ )
198
+ self.pos_args[name] = arg
199
+ setattr(self.result, name, default)
200
+ return None
201
+
202
+ def arg_int(self, *, short: str = None, long: str = None, default=None, desc=None):
203
+ """
204
+ Defines an optional integer argument.
205
+
206
+ Args:
207
+ short (str, optional): Short name for the argument (e.g., `-n`).
208
+ long (str, optional): Long name for the argument (e.g., `--number`).
209
+ default (int, required): Default integer value for the argument.
210
+ desc (str, optional): Description for the argument, used in help messages.
211
+
212
+ Raises:
213
+ TypeError: If the provided default value is not an integer.
214
+
215
+ Examples:
216
+ >>> am = ArgMan()
217
+ >>> am.arg_int(short='n', long='node', default=3, desc='Number of nodes')
218
+ >>> args = am.parse()
219
+ >>> print(args.node)
220
+ 3
221
+ """
222
+ if default is not None and not isinstance(default, int):
223
+ raise TypeError("default must be an int")
224
+ self.__set_arg(int, short, long, default, desc)
225
+ return None
226
+
227
+ def arg_float(self, *, short: str = None, long: str = None, default=None, desc=None):
228
+ """
229
+ Defines an optional float argument.
230
+
231
+ Args:
232
+ short (str, optional): Short name for the argument (e.g., `-r`).
233
+ long (str, optional): Long name for the argument (e.g., `--rate`).
234
+ default (float, required): Default float value for the argument.
235
+ desc (str, optional): Description for the argument, used in help messages.
236
+
237
+ Raises:
238
+ TypeError: If the provided default value is not a number.
239
+
240
+ Examples:
241
+ >>> am = ArgMan()
242
+ >>> am.arg_float(short='r', long='rate', default=1.0, desc='Payment rate')
243
+ >>> args = am.parse()
244
+ >>> print(args.rate)
245
+ 1.0
246
+ """
247
+ if default is not None and not isinstance(default, (float, int)):
248
+ raise TypeError("default must be a number")
249
+ self.__set_arg(float, short, long, float(default), desc)
250
+ return None
251
+
252
+ def arg_str(self, *, short: str = None, long: str = None, default=None, desc=None):
253
+ """
254
+ Defines an optional string argument.
255
+
256
+ Args:
257
+ short (str, optional): Short name for the argument (e.g., `-a`).
258
+ long (str, optional): Long name for the argument (e.g., `--author`).
259
+ default (str, required): Default str value for the argument.
260
+ desc (str, optional): Description for the argument, used in help messages.
261
+
262
+ Raises:
263
+ TypeError: If the provided default value is not an string.
264
+
265
+ Examples:
266
+ >>> am = ArgMan()
267
+ >>> am.arg_str(short='a', long='author', default='John Doe', desc='Author name')
268
+ >>> args = am.parse()
269
+ >>> print(args.author)
270
+ 'John Doe'
271
+ """
272
+ if default is not None and not isinstance(default, str):
273
+ raise TypeError("default must be a str")
274
+ self.__set_arg(str, short, long, default, desc)
275
+ return None
276
+
277
+ def arg_bool(self, *, short: str = None, long: str = None, default=False, desc=None):
278
+ """
279
+ Defines an optional boolean argument.
280
+
281
+ Args:
282
+ short (str, optional): Short name for the argument (e.g., `-r`).
283
+ long (str, optional): Long name for the argument (e.g., `--run`).
284
+ default (bool, required): Default bool value for the argument.
285
+ desc (str, optional): Description for the argument, used in help messages.
286
+
287
+ Behavior:
288
+ - If `default=False` (default), the flag enables the feature:
289
+ `--run` → `run = True`.
290
+ - If `default=True`, the flag still enables the feature (`--verbose` → `verbose = True`),
291
+ and a negation flag `--no-<long>` is **automatically added** to disable it:
292
+ `--no-verbose` → `verbose = False`.
293
+ - The `--no-<long>` negation flag is **only generated if `long` is provided**.
294
+ Short-only boolean flags (e.g., `-v` with no `--verbose`) cannot be negated.
295
+
296
+ Raises:
297
+ TypeError: If the provided default value is not a bool.
298
+ ValueError: If neither `short` nor `long` is provided, or if name lengths are invalid.
299
+
300
+ Examples:
301
+ >>> am = ArgMan()
302
+ >>> am.arg_bool(short='r', long='run', default=False, desc='Run the program')
303
+ >>> am.arg_bool(long='verbose', default=True, desc='Enable verbose output')
304
+ >>> args = am.parse()
305
+ >>> print(args.run) # False by default, True if --run is passed
306
+ >>> print(args.verbose) # True by default, False if --no-verbose is passed
307
+ """
308
+ if not isinstance(default, bool):
309
+ raise TypeError("default must be a bool")
310
+ self.__set_arg(bool, short, long, default, desc)
311
+ if default is True and long is not None:
312
+ no_long = f"no-{long}"
313
+ self.aliases[no_long] = long
314
+ return None
315
+
316
+ def arg_list(self, *, short: str = None, long: str = None, default=None, item_type: type = str, desc=None):
317
+ """
318
+ Defines an optional list argument.
319
+
320
+ Args:
321
+ short (str, optional): Short name for the argument (e.g., `-f`).
322
+ long (str, optional): Long name for the argument (e.g., `--file`).
323
+ default (list, optional): Default list value for the argument.
324
+ desc (str, optional): Description for the argument, used in help messages.
325
+ item_type (type, optional): Type to which each value should be converted (default: str).
326
+
327
+ Raises:
328
+ TypeError: If the provided default value is not a list.
329
+
330
+ Examples:
331
+ >>> am = ArgMan()
332
+ >>> am.arg_list(short='f', long='files', default=[], desc='List of input files')
333
+ >>> # Simulating: python script.py --files a.txt --files b.txt
334
+ >>> am.argv = ['--files', 'a.txt', '--files', 'b.txt']
335
+ >>> args = am.parse()
336
+ >>> print(args.files)
337
+ ['a.txt', 'b.txt']
338
+ """
339
+ if default is None:
340
+ default = list()
341
+ else:
342
+ if not isinstance(default, list):
343
+ raise TypeError("default must be a list")
344
+ self.__set_arg(list, short, long, default, desc, item_type)
345
+ return None
346
+
347
+ # TODO: make this function work without length check, to make it smaller
348
+ # TODO: better handling jumps and issues
349
+ def _parse_short_arg(self, short_arg: str, next_arg: str = None):
350
+ if '=' in short_arg:
351
+ raise ArgParseError(
352
+ f"Short option '{short_arg.split('=')[0]}' does not support '=' syntax. Use space-separated values")
353
+ jump = 0
354
+ name = short_arg.removeprefix('-')
355
+ if len(name) > 1:
356
+ jump = 1
357
+ i = 0
358
+ while i < len(name):
359
+ arg_name = name[i]
360
+ alias = self.aliases.get(arg_name)
361
+ if alias is None:
362
+ raise ArgParseError(f"Unknown argument '-{arg_name}' in '-{name}'")
363
+ arg = self.args.get(alias)
364
+ if arg.type is bool:
365
+ arg_value = True
366
+ i += 1
367
+ else:
368
+ raise ArgParseError(
369
+ f"Option '-{arg_name}' requires an argument and cannot be clustered with other short options.")
370
+
371
+ setattr(self.result, arg.short, arg_value)
372
+ if arg.long is not None:
373
+ setattr(self.result, arg.long, arg_value)
374
+ else:
375
+ arg_name = self.aliases.get(name)
376
+ if arg_name is None:
377
+ raise ArgParseError(f"Unknown argument '-{name}'")
378
+ arg = self.args.get(arg_name)
379
+ if arg.type is bool:
380
+ arg_value = True
381
+ jump = 1
382
+ setattr(self.result, arg.short, arg_value)
383
+ if arg.long:
384
+ setattr(self.result, arg.long, arg_value)
385
+ return jump
386
+ else:
387
+ if next_arg is None:
388
+ raise ArgParseError(f"Missing value for argument '-{name}'")
389
+ arg_value = next_arg
390
+ jump = 2
391
+ if arg.type is not list:
392
+ try:
393
+ arg_value = arg.type(next_arg)
394
+ except ValueError:
395
+ raise ArgParseError(
396
+ f"Value should be a {arg.type.__name__}. argument '{arg.long or arg.short}'")
397
+ else:
398
+ values = getattr(self.result, arg_name, [])
399
+ try:
400
+ casted_value = arg.item_type(arg_value)
401
+ except ValueError:
402
+ raise ArgParseError(f"Value '{arg_value}' should be of type {arg.item_type.__name__}")
403
+ values.append(casted_value)
404
+ arg_value = values
405
+ setattr(self.result, arg.short, arg_value)
406
+ if arg.long is not None:
407
+ setattr(self.result, arg.long, arg_value)
408
+ return jump
409
+
410
+ def _parse_long_arg(self, long_arg: str, next_arg: str = None):
411
+ jump = 1
412
+ name = long_arg.removeprefix('--')
413
+ arg_name = self.aliases.get(name)
414
+ if arg_name is None:
415
+ raise ArgParseError(f"Unknown argument '--{name}'")
416
+ arg = self.args.get(arg_name)
417
+ if arg.type is bool:
418
+ if long_arg.startswith('--no-'):
419
+ arg_value = False
420
+ else:
421
+ arg_value = True
422
+ setattr(self.result, arg.long, arg_value)
423
+ if arg.short:
424
+ setattr(self.result, arg.short, arg_value)
425
+ return jump
426
+ if next_arg is None:
427
+ raise ArgParseError(f"Missing value for argument '--{name}'")
428
+ arg_value = next_arg
429
+ jump = 2
430
+ if arg.type is not list:
431
+ try:
432
+ arg_value = arg.type(next_arg)
433
+ setattr(self.result, arg.long, arg_value)
434
+ if arg.short:
435
+ setattr(self.result, arg.short, arg_value)
436
+ except ValueError:
437
+ raise ArgParseError(
438
+ f"Value should be a {arg.type.__name__}. argument '{arg.long}'"
439
+ )
440
+ else:
441
+ values = getattr(self.result, arg_name, [])
442
+ try:
443
+ casted_value = arg.item_type(arg_value)
444
+ except ValueError:
445
+ raise ArgParseError(f"Value '{arg_value}' should be of type {arg.item_type.__name__}")
446
+ values.append(casted_value)
447
+ setattr(self.result, arg.long, values)
448
+ if arg.short:
449
+ setattr(self.result, arg.short, values)
450
+ return jump
451
+
452
+ def _parse_pos_arg(self, arg):
453
+ if len(self.pos_args) < 1:
454
+ raise ArgParseError(f"Unknown argument '{arg}'")
455
+ name = _arg = None
456
+
457
+ req = [a for a in self.pos_args.values() if not a.parsed and a.required]
458
+ non_req = [a for a in self.pos_args.values() if not a.parsed and not a.required]
459
+ for a in req:
460
+ name = a.name
461
+ _arg = a
462
+ self.pos_args[name].parsed = True
463
+ break
464
+ else:
465
+ for a in non_req:
466
+ name = a.name
467
+ _arg = a
468
+ self.pos_args[name].parsed = True
469
+ break
470
+ else:
471
+ raise ArgParseError(f"Unknown argument '{arg}'")
472
+ try:
473
+ value = _arg.type(arg)
474
+ except ValueError:
475
+ raise ArgParseError(f"Type mismatch for '{_arg.name}' (expected {_arg.type.__name__})")
476
+ setattr(self.result, name, value)
477
+
478
+ def parse(self):
479
+ """
480
+ Parses the command-line arguments provided to the program.
481
+
482
+ This method reads arguments from `sys.argv`, matches them against the
483
+ defined options (via `arg_int`, `arg_float`, `arg_str`, or `arg_bool`),
484
+ and returns an object containing the parsed results.
485
+ Each argument value is automatically converted to its defined type.
486
+
487
+ Returns:
488
+ _ArgResult: An object with attributes corresponding to defined arguments,
489
+ holding their parsed or default values.
490
+
491
+ Raises:
492
+ ValueError: If a non-boolean argument is missing a required value or if
493
+ a value cannot be converted to the expected type.
494
+
495
+ Example:
496
+ >>> # Example usage
497
+ >>> am = ArgMan()
498
+ >>> am.arg_int(short='n', long='num', default=10, desc='Number of items')
499
+ >>> am.arg_bool(short='v', long='verbose', default=False, desc='Enable verbose mode')
500
+ >>> args = am.parse()
501
+ >>> print(args.num, args.verbose)
502
+ 10 False
503
+ """
504
+
505
+ i = 0
506
+ while i < len(self.argv):
507
+ arg = self.argv[i]
508
+ if self.pos_only:
509
+ i += 1
510
+ try:
511
+ self._parse_pos_arg(arg)
512
+ continue
513
+ except ArgParseError as e:
514
+ self._print_err(str(e))
515
+
516
+ if arg == '--help':
517
+ self._print_help()
518
+ exit(0)
519
+
520
+ if arg == '--':
521
+ self.pos_only = True
522
+ i += 1
523
+ continue
524
+ if arg.startswith('--'):
525
+ if '=' in arg:
526
+ arg, next_arg = arg.split('=', 1)
527
+ try:
528
+ self._parse_long_arg(arg, next_arg)
529
+ i += 1
530
+ continue
531
+ except ArgParseError as e:
532
+ self._print_err(str(e))
533
+ else:
534
+ next_arg = self.argv[i + 1] if i + 1 < len(self.argv) else None
535
+ try:
536
+ jump = self._parse_long_arg(arg, next_arg)
537
+ i += jump
538
+ continue
539
+ except ArgParseError as e:
540
+ self._print_err(str(e))
541
+ elif arg.startswith('-'):
542
+ next_arg = None
543
+ if i + 1 < len(self.argv):
544
+ next_arg = self.argv[i + 1]
545
+ try:
546
+ jump = self._parse_short_arg(arg, next_arg)
547
+ i += jump
548
+ continue
549
+ except ArgParseError as e:
550
+ self._print_err(str(e))
551
+ else:
552
+ i += 1
553
+ try:
554
+ self._parse_pos_arg(arg)
555
+ continue
556
+ except ArgParseError as e:
557
+ self._print_err(str(e))
558
+
559
+ raise AssertionError(f"Unreachable '{arg}'")
560
+ missing = [n for n, a in self.pos_args.items() if not a.parsed and a.required and not a.default]
561
+ if len(missing) > 0:
562
+ message = "Missing required arguments:\n"
563
+ message += '\n'.join([f" {name}" for name in missing])
564
+ self._print_err(message)
565
+ return self.result
@@ -0,0 +1,70 @@
1
+ Metadata-Version: 2.4
2
+ Name: argman
3
+ Version: 0.2.0
4
+ Summary: A lightweight argument manager for Python CLI tools
5
+ Author-email: Seyed Mohammad Javad Taghipoor <smjavadt.1379@gmail.com>
6
+ License: LGPL-3.0-or-later
7
+ Project-URL: Homepage, https://github.com/smjt2000/argman
8
+ Keywords: cli,argparse,arguments,parser
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.10
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+ Dynamic: license-file
16
+
17
+ # ArgMan
18
+
19
+ A lightweight, zero-dependency argument parser for Python CLI tools — simple, testable, and intuitive.
20
+
21
+ ---
22
+
23
+ ## Features
24
+
25
+ - Short (`-v`) and long (`--verbose`) flags
26
+ - Type-safe parsing: `int`, `float`, `str`, `bool`, `list`
27
+ - Default values and boolean toggles
28
+ - Repeated arguments with `arg_list`
29
+ - Positional arguments
30
+ - Automatic `--no-flag` for booleans
31
+ - `--arg=value` and `--` terminator support
32
+ - Clean help and error messages
33
+
34
+ ---
35
+
36
+ ## Documentation
37
+
38
+ Usage examples and API reference: [docs/](docs/)
39
+
40
+ ---
41
+
42
+ ## Running Tests
43
+
44
+ ```bash
45
+ python -m unittest discover tests
46
+ ```
47
+
48
+ ## Roadmap
49
+
50
+ v0.1 — Core Functionality
51
+
52
+ - [x] Complete
53
+
54
+ v0.2 — Extended Features
55
+
56
+ - [x] arg_list, --no-flag, --arg=value, -- support
57
+ - [ ] Custom error messages, config files, subcommands
58
+
59
+ v0.3 — Docs & Publish
60
+
61
+ - [x] Docs ready
62
+ - [ ] Publish to PyPI
63
+
64
+ v0.4 — Validation & Customization
65
+
66
+ - [ ] Validators, formatters, dependencies, custom handlers
67
+
68
+ ## License
69
+
70
+ LGPL-3.0 © 2025
@@ -0,0 +1,7 @@
1
+ argman/__init__.py,sha256=gIxpHW5GZ8xtJqX8Y1zPTHL8Dl_BqBRyF45sVPzsupU,41
2
+ argman/argman.py,sha256=DhKSdeXjDuHDExXPPFNEdSOlCLNLiQD2wStogjYfTUE,21997
3
+ argman-0.2.0.dist-info/licenses/LICENSE,sha256=Vy29C7JzvqgdHXBAvpcyE54fbUHEk_wnG6GZen_t9qo,9151
4
+ argman-0.2.0.dist-info/METADATA,sha256=ggExkbSaoE3Hn5_e6tO0gBP0OEXjqR_b56baKlVlTrI,1581
5
+ argman-0.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
6
+ argman-0.2.0.dist-info/top_level.txt,sha256=pWXM4i7QbUg7HVlwqlutatPTNXCHfxjmVK6y9Ra_Z8Q,7
7
+ argman-0.2.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,198 @@
1
+ ---
2
+ title: GNU Lesser General Public License v3.0
3
+ spdx-id: LGPL-3.0
4
+ nickname: GNU LGPLv3
5
+ redirect_from: /licenses/lgpl-v3/
6
+
7
+ description: Permissions of this copyleft license are conditioned on making available complete source code of licensed works and modifications under the same license or the GNU GPLv3. Copyright and license notices must be preserved. Contributors provide an express grant of patent rights. However, a larger work using the licensed work through interfaces provided by the licensed work may be distributed under different terms and without source code for the larger work.
8
+
9
+ how: This license is an additional set of permissions to the <a href="/licenses/gpl-3.0">GNU GPLv3</a> license. Follow the instructions to apply the GNU GPLv3, in the root of your source code. Then add another file named COPYING.LESSER and copy the text.
10
+
11
+ note: The Free Software Foundation recommends taking the additional step of adding a boilerplate notice to the top of each file. The boilerplate can be found at the end of the <a href="/licenses/gpl-3.0">GNU GPLv3 license</a>. Insert the word “Lesser” before “General” in all three places in the boilerplate notice to make sure that you refer to the GNU LGPLv3 and not the GNU GPLv3.
12
+
13
+ using:
14
+
15
+ permissions:
16
+ - commercial-use
17
+ - modifications
18
+ - distribution
19
+ - patent-use
20
+ - private-use
21
+
22
+ conditions:
23
+ - include-copyright
24
+ - disclose-source
25
+ - document-changes
26
+ - same-license--library
27
+
28
+ limitations:
29
+ - liability
30
+ - warranty
31
+
32
+ ---
33
+
34
+ GNU LESSER GENERAL PUBLIC LICENSE
35
+ Version 3, 29 June 2007
36
+
37
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
38
+ Everyone is permitted to copy and distribute verbatim copies
39
+ of this license document, but changing it is not allowed.
40
+
41
+
42
+ This version of the GNU Lesser General Public License incorporates
43
+ the terms and conditions of version 3 of the GNU General Public
44
+ License, supplemented by the additional permissions listed below.
45
+
46
+ 0. Additional Definitions.
47
+
48
+ As used herein, "this License" refers to version 3 of the GNU Lesser
49
+ General Public License, and the "GNU GPL" refers to version 3 of the GNU
50
+ General Public License.
51
+
52
+ "The Library" refers to a covered work governed by this License,
53
+ other than an Application or a Combined Work as defined below.
54
+
55
+ An "Application" is any work that makes use of an interface provided
56
+ by the Library, but which is not otherwise based on the Library.
57
+ Defining a subclass of a class defined by the Library is deemed a mode
58
+ of using an interface provided by the Library.
59
+
60
+ A "Combined Work" is a work produced by combining or linking an
61
+ Application with the Library. The particular version of the Library
62
+ with which the Combined Work was made is also called the "Linked
63
+ Version".
64
+
65
+ The "Minimal Corresponding Source" for a Combined Work means the
66
+ Corresponding Source for the Combined Work, excluding any source code
67
+ for portions of the Combined Work that, considered in isolation, are
68
+ based on the Application, and not on the Linked Version.
69
+
70
+ The "Corresponding Application Code" for a Combined Work means the
71
+ object code and/or source code for the Application, including any data
72
+ and utility programs needed for reproducing the Combined Work from the
73
+ Application, but excluding the System Libraries of the Combined Work.
74
+
75
+ 1. Exception to Section 3 of the GNU GPL.
76
+
77
+ You may convey a covered work under sections 3 and 4 of this License
78
+ without being bound by section 3 of the GNU GPL.
79
+
80
+ 2. Conveying Modified Versions.
81
+
82
+ If you modify a copy of the Library, and, in your modifications, a
83
+ facility refers to a function or data to be supplied by an Application
84
+ that uses the facility (other than as an argument passed when the
85
+ facility is invoked), then you may convey a copy of the modified
86
+ version:
87
+
88
+ a) under this License, provided that you make a good faith effort to
89
+ ensure that, in the event an Application does not supply the
90
+ function or data, the facility still operates, and performs
91
+ whatever part of its purpose remains meaningful, or
92
+
93
+ b) under the GNU GPL, with none of the additional permissions of
94
+ this License applicable to that copy.
95
+
96
+ 3. Object Code Incorporating Material from Library Header Files.
97
+
98
+ The object code form of an Application may incorporate material from
99
+ a header file that is part of the Library. You may convey such object
100
+ code under terms of your choice, provided that, if the incorporated
101
+ material is not limited to numerical parameters, data structure
102
+ layouts and accessors, or small macros, inline functions and templates
103
+ (ten or fewer lines in length), you do both of the following:
104
+
105
+ a) Give prominent notice with each copy of the object code that the
106
+ Library is used in it and that the Library and its use are
107
+ covered by this License.
108
+
109
+ b) Accompany the object code with a copy of the GNU GPL and this license
110
+ document.
111
+
112
+ 4. Combined Works.
113
+
114
+ You may convey a Combined Work under terms of your choice that,
115
+ taken together, effectively do not restrict modification of the
116
+ portions of the Library contained in the Combined Work and reverse
117
+ engineering for debugging such modifications, if you also do each of
118
+ the following:
119
+
120
+ a) Give prominent notice with each copy of the Combined Work that
121
+ the Library is used in it and that the Library and its use are
122
+ covered by this License.
123
+
124
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
125
+ document.
126
+
127
+ c) For a Combined Work that displays copyright notices during
128
+ execution, include the copyright notice for the Library among
129
+ these notices, as well as a reference directing the user to the
130
+ copies of the GNU GPL and this license document.
131
+
132
+ d) Do one of the following:
133
+
134
+ 0) Convey the Minimal Corresponding Source under the terms of this
135
+ License, and the Corresponding Application Code in a form
136
+ suitable for, and under terms that permit, the user to
137
+ recombine or relink the Application with a modified version of
138
+ the Linked Version to produce a modified Combined Work, in the
139
+ manner specified by section 6 of the GNU GPL for conveying
140
+ Corresponding Source.
141
+
142
+ 1) Use a suitable shared library mechanism for linking with the
143
+ Library. A suitable mechanism is one that (a) uses at run time
144
+ a copy of the Library already present on the user's computer
145
+ system, and (b) will operate properly with a modified version
146
+ of the Library that is interface-compatible with the Linked
147
+ Version.
148
+
149
+ e) Provide Installation Information, but only if you would otherwise
150
+ be required to provide such information under section 6 of the
151
+ GNU GPL, and only to the extent that such information is
152
+ necessary to install and execute a modified version of the
153
+ Combined Work produced by recombining or relinking the
154
+ Application with a modified version of the Linked Version. (If
155
+ you use option 4d0, the Installation Information must accompany
156
+ the Minimal Corresponding Source and Corresponding Application
157
+ Code. If you use option 4d1, you must provide the Installation
158
+ Information in the manner specified by section 6 of the GNU GPL
159
+ for conveying Corresponding Source.)
160
+
161
+ 5. Combined Libraries.
162
+
163
+ You may place library facilities that are a work based on the
164
+ Library side by side in a single library together with other library
165
+ facilities that are not Applications and are not covered by this
166
+ License, and convey such a combined library under terms of your
167
+ choice, if you do both of the following:
168
+
169
+ a) Accompany the combined library with a copy of the same work based
170
+ on the Library, uncombined with any other library facilities,
171
+ conveyed under the terms of this License.
172
+
173
+ b) Give prominent notice with the combined library that part of it
174
+ is a work based on the Library, and explaining where to find the
175
+ accompanying uncombined form of the same work.
176
+
177
+ 6. Revised Versions of the GNU Lesser General Public License.
178
+
179
+ The Free Software Foundation may publish revised and/or new versions
180
+ of the GNU Lesser General Public License from time to time. Such new
181
+ versions will be similar in spirit to the present version, but may
182
+ differ in detail to address new problems or concerns.
183
+
184
+ Each version is given a distinguishing version number. If the
185
+ Library as you received it specifies that a certain numbered version
186
+ of the GNU Lesser General Public License "or any later version"
187
+ applies to it, you have the option of following the terms and
188
+ conditions either of that published version or of any later version
189
+ published by the Free Software Foundation. If the Library as you
190
+ received it does not specify a version number of the GNU Lesser
191
+ General Public License, you may choose any version of the GNU Lesser
192
+ General Public License ever published by the Free Software Foundation.
193
+
194
+ If the Library as you received it specifies that a proxy can decide
195
+ whether future versions of the GNU Lesser General Public License shall
196
+ apply, that proxy's public statement of acceptance of any version is
197
+ permanent authorization for you to choose that version for the
198
+ Library.
@@ -0,0 +1 @@
1
+ argman