log21 3.0.2__tar.gz → 3.1.0__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: log21
3
- Version: 3.0.2
3
+ Version: 3.1.0
4
4
  Summary: A simple logging package
5
5
  Keywords: python,log,colorize,color,logging,Python3,CodeWriter21
6
6
  Author: CodeWriter21(Mehrad Pooryoussof)
@@ -94,57 +94,140 @@ pip install git+https://github.com/MPCodeWriter21/log21
94
94
  Changelog
95
95
  ---------
96
96
 
97
- ### v3.0.2
97
+ ### v3.1.0
98
98
 
99
- Change `argumentify` to use the whole function description as the argument-parser
100
- description instead of the one-line short description.
99
+ Change the way `argumentify` handles function parameters to argument-parser arguments
100
+ conversion.
101
101
 
102
- + Example:
102
+ + `POSITIONAL_ONLY` and `VAR_POSITIONAL` parameters will be positional arguments.
103
+ + `POSITIONAL_OR_KEYWORD` and `KEYWORD_ONLY` parameters have flags assigned to them.
104
+ + `POSITIONAL_OR_KEYWORD` parameters will be required if at least one `KEYWORD_ONLY`
105
+ parameter is there, otherwise they are optional.
106
+ + `VAR_KEYWORD` parameters are still not supported.
103
107
 
104
- ```python
105
- def main(verbose: bool = False) -> None:
106
- """This is a very useful tool and I will describe it thoroughly. It is so good that
107
- we have a second line in the first part of the description.
108
+ #### Example 1
108
109
 
109
- And now we can talk more about the tool...
110
+ ```python
111
+ def main(path: Path, /, output: Path, *, verbose: bool = False):
112
+ """Process a file.
110
113
 
111
- :param verbose: This flag will make the logs more verbose!
114
+ :param path: The input file path
115
+ :param output: The output file
116
+ :param verbose: Write more logs to the standard output.
112
117
  """
118
+ ...
113
119
 
114
- argumentify(main)
120
+
121
+ if __name__ == "__main__":
122
+ argumentify(main)
115
123
  ```
116
124
 
117
- The way old versions would look:
125
+ The help looks like this:
118
126
 
119
127
  ```help
120
- usage: test.py [-h] [--verbose]
128
+ usage: test.py [-h] --output OUTPUT [--verbose] path
129
+
130
+ Process a file.
121
131
 
122
- This is a very useful tool and I will describe it thoroughly. It is so good that
132
+ positional arguments:
133
+ path The input file path
123
134
 
124
135
  options:
125
136
  -h, --help
126
137
  show this help message and exit
138
+ --output OUTPUT, -o OUTPUT
139
+ The output file
127
140
  --verbose, -v
128
- This flag will make the logs more verbose!
141
+ Write more logs to the standard output.
142
+
143
+ ```
144
+
145
+ _Note that `path` and `output` are required._
146
+
147
+ #### Example 2
129
148
 
149
+ ```python
150
+ def main(output: Path, /, *inputs: Path):
151
+ """Process multiple files into one.
152
+
153
+ :param output: The output file
154
+ :param inputs: The path to the input files
155
+ """
156
+ # Since `inputs` is a VAR_POSITIONAL, while being a positional argument, it can have
157
+ # zero length which is in many cases not intended.
158
+ # You might want to add a check for its length and raise an ArgumentError if it does
159
+ # not match your needs
160
+
161
+ # Check if at least one input has been passed and mark the argument as required
162
+ # if len(inputs) < 1:
163
+ # raise RequiredArgumentError("inputs")
164
+
165
+ # Raise an error unless at least two inputs are present
166
+ if len(inputs) < 2:
167
+ raise ArgumentError(message="You need to pass at least two files as input.")
168
+ ...
130
169
  ```
131
170
 
132
- Now at v3.0.2:
171
+ The help looks like this:
133
172
 
134
173
  ```help
135
- usage: test.py [-h] [--verbose]
174
+ usage: test.py [-h] output [inputs ...]
175
+
176
+ Process multiple files into one.
136
177
 
137
- This is a very useful tool and I will describe it thoroughly. It is so good that we have a
138
- second line in the first part of the description. And now we can talk more about the tool...
178
+ positional arguments:
179
+ output The output file
180
+ inputs The path to the input files
139
181
 
140
182
  options:
141
183
  -h, --help
142
184
  show this help message and exit
185
+
186
+ ```
187
+
188
+ #### Example 3
189
+
190
+ ```python
191
+ def main(first_name: str, last_name: str, output: Path, verbose: bool = False):
192
+ """Write a greeting message.
193
+
194
+ :param first_name: The first name of the user to greet (optional)
195
+ :param last_name: The last name of the user to greet (optional)
196
+ :param output: The output file (stdout if none is provided)
197
+ :param verbose: If provided, will write the debug logs to stdout
198
+ """
199
+ ...
200
+
201
+
202
+ if __name__ == "__main__":
203
+ argumentify(main)
204
+ ```
205
+
206
+ The help looks like this:
207
+
208
+ ```help
209
+ usage: test.py [-h] [--first-name FIRST_NAME] [--last-name LAST_NAME] [--output OUTPUT]
210
+ [--verbose]
211
+
212
+ Write a greeting message.
213
+
214
+ options:
215
+ -h, --help
216
+ show this help message and exit
217
+ --first-name FIRST_NAME, -f FIRST_NAME
218
+ The first name of the user to greet (optional)
219
+ --last-name LAST_NAME, -l LAST_NAME
220
+ The last name of the user to greet (optional)
221
+ --output OUTPUT, -o OUTPUT
222
+ The output file (stdout if none is provided)
143
223
  --verbose, -v
144
- This flag will make the logs more verbose!
224
+ If provided, will write the debug logs to stdout
145
225
 
146
226
  ```
147
227
 
228
+ _Note that all the options are optional and default to None. `verbose` is False by
229
+ default since a default value is provided for it in function definition._
230
+
148
231
  [Full CHANGELOG](https://github.com/MPCodeWriter21/log21/blob/master/CHANGELOG.md)
149
232
 
150
233
  Usage Examples
@@ -69,57 +69,140 @@ pip install git+https://github.com/MPCodeWriter21/log21
69
69
  Changelog
70
70
  ---------
71
71
 
72
- ### v3.0.2
72
+ ### v3.1.0
73
73
 
74
- Change `argumentify` to use the whole function description as the argument-parser
75
- description instead of the one-line short description.
74
+ Change the way `argumentify` handles function parameters to argument-parser arguments
75
+ conversion.
76
76
 
77
- + Example:
77
+ + `POSITIONAL_ONLY` and `VAR_POSITIONAL` parameters will be positional arguments.
78
+ + `POSITIONAL_OR_KEYWORD` and `KEYWORD_ONLY` parameters have flags assigned to them.
79
+ + `POSITIONAL_OR_KEYWORD` parameters will be required if at least one `KEYWORD_ONLY`
80
+ parameter is there, otherwise they are optional.
81
+ + `VAR_KEYWORD` parameters are still not supported.
78
82
 
79
- ```python
80
- def main(verbose: bool = False) -> None:
81
- """This is a very useful tool and I will describe it thoroughly. It is so good that
82
- we have a second line in the first part of the description.
83
+ #### Example 1
83
84
 
84
- And now we can talk more about the tool...
85
+ ```python
86
+ def main(path: Path, /, output: Path, *, verbose: bool = False):
87
+ """Process a file.
85
88
 
86
- :param verbose: This flag will make the logs more verbose!
89
+ :param path: The input file path
90
+ :param output: The output file
91
+ :param verbose: Write more logs to the standard output.
87
92
  """
93
+ ...
88
94
 
89
- argumentify(main)
95
+
96
+ if __name__ == "__main__":
97
+ argumentify(main)
90
98
  ```
91
99
 
92
- The way old versions would look:
100
+ The help looks like this:
93
101
 
94
102
  ```help
95
- usage: test.py [-h] [--verbose]
103
+ usage: test.py [-h] --output OUTPUT [--verbose] path
104
+
105
+ Process a file.
96
106
 
97
- This is a very useful tool and I will describe it thoroughly. It is so good that
107
+ positional arguments:
108
+ path The input file path
98
109
 
99
110
  options:
100
111
  -h, --help
101
112
  show this help message and exit
113
+ --output OUTPUT, -o OUTPUT
114
+ The output file
102
115
  --verbose, -v
103
- This flag will make the logs more verbose!
116
+ Write more logs to the standard output.
117
+
118
+ ```
119
+
120
+ _Note that `path` and `output` are required._
121
+
122
+ #### Example 2
104
123
 
124
+ ```python
125
+ def main(output: Path, /, *inputs: Path):
126
+ """Process multiple files into one.
127
+
128
+ :param output: The output file
129
+ :param inputs: The path to the input files
130
+ """
131
+ # Since `inputs` is a VAR_POSITIONAL, while being a positional argument, it can have
132
+ # zero length which is in many cases not intended.
133
+ # You might want to add a check for its length and raise an ArgumentError if it does
134
+ # not match your needs
135
+
136
+ # Check if at least one input has been passed and mark the argument as required
137
+ # if len(inputs) < 1:
138
+ # raise RequiredArgumentError("inputs")
139
+
140
+ # Raise an error unless at least two inputs are present
141
+ if len(inputs) < 2:
142
+ raise ArgumentError(message="You need to pass at least two files as input.")
143
+ ...
105
144
  ```
106
145
 
107
- Now at v3.0.2:
146
+ The help looks like this:
108
147
 
109
148
  ```help
110
- usage: test.py [-h] [--verbose]
149
+ usage: test.py [-h] output [inputs ...]
150
+
151
+ Process multiple files into one.
111
152
 
112
- This is a very useful tool and I will describe it thoroughly. It is so good that we have a
113
- second line in the first part of the description. And now we can talk more about the tool...
153
+ positional arguments:
154
+ output The output file
155
+ inputs The path to the input files
114
156
 
115
157
  options:
116
158
  -h, --help
117
159
  show this help message and exit
160
+
161
+ ```
162
+
163
+ #### Example 3
164
+
165
+ ```python
166
+ def main(first_name: str, last_name: str, output: Path, verbose: bool = False):
167
+ """Write a greeting message.
168
+
169
+ :param first_name: The first name of the user to greet (optional)
170
+ :param last_name: The last name of the user to greet (optional)
171
+ :param output: The output file (stdout if none is provided)
172
+ :param verbose: If provided, will write the debug logs to stdout
173
+ """
174
+ ...
175
+
176
+
177
+ if __name__ == "__main__":
178
+ argumentify(main)
179
+ ```
180
+
181
+ The help looks like this:
182
+
183
+ ```help
184
+ usage: test.py [-h] [--first-name FIRST_NAME] [--last-name LAST_NAME] [--output OUTPUT]
185
+ [--verbose]
186
+
187
+ Write a greeting message.
188
+
189
+ options:
190
+ -h, --help
191
+ show this help message and exit
192
+ --first-name FIRST_NAME, -f FIRST_NAME
193
+ The first name of the user to greet (optional)
194
+ --last-name LAST_NAME, -l LAST_NAME
195
+ The last name of the user to greet (optional)
196
+ --output OUTPUT, -o OUTPUT
197
+ The output file (stdout if none is provided)
118
198
  --verbose, -v
119
- This flag will make the logs more verbose!
199
+ If provided, will write the debug logs to stdout
120
200
 
121
201
  ```
122
202
 
203
+ _Note that all the options are optional and default to None. `verbose` is False by
204
+ default since a default value is provided for it in function definition._
205
+
123
206
  [Full CHANGELOG](https://github.com/MPCodeWriter21/log21/blob/master/CHANGELOG.md)
124
207
 
125
208
  Usage Examples
@@ -23,7 +23,7 @@ dependencies = [
23
23
  "webcolors",
24
24
  "docstring-parser"
25
25
  ]
26
- version = "3.0.2"
26
+ version = "3.1.0"
27
27
 
28
28
  [build-system]
29
29
  requires = ["uv_build>=0.8.15,<0.9.0"]
@@ -31,7 +31,7 @@ from .stream_handler import StreamHandler, ColorizingStreamHandler
31
31
  # yapf: enable
32
32
 
33
33
  __author__ = 'CodeWriter21 (Mehrad Pooryoussof)'
34
- __version__ = '3.0.2'
34
+ __version__ = '3.1.0'
35
35
  __github__ = 'https://GitHub.com/MPCodeWriter21/log21'
36
36
  __all__ = [
37
37
  'ColorizingStreamHandler', 'DecolorizingFileHandler', 'ColorizingFormatter',
@@ -344,6 +344,28 @@ def _add_arguments(
344
344
  """
345
345
  if reserved_flags is None:
346
346
  reserved_flags = RESERVED_FLAGS.copy()
347
+
348
+ keyword_only_exists = False
349
+ for argument in info.arguments.values():
350
+ # Reserve the name of POSITIONAL_ONLY and VAR_POSITIONAL arguments as flags
351
+ # since their flag name and their dest need to be the same
352
+ if argument.kind in [_inspect._ParameterKind.POSITIONAL_ONLY,
353
+ _inspect._ParameterKind.VAR_POSITIONAL]:
354
+ reserved_flags.add(argument.name)
355
+ # If there is at least one KEYWORD_ONLY argument, the parameters of kind
356
+ # POSITIONAL_OR_KEYWORD will be marked as required
357
+ if argument.kind == _inspect._ParameterKind.KEYWORD_ONLY:
358
+ keyword_only_exists = True
359
+ # Check if the function has a VAR_KEYWORD argument
360
+ # Raises a ArgumentTypeError if it does
361
+ # TODO: See if we can find a use-case and a way of supporting these arguments
362
+ if argument.kind == _inspect._ParameterKind.VAR_KEYWORD:
363
+ raise ArgumentTypeError(
364
+ f"The function has a `**{argument.name}` argument, "
365
+ "which is not supported.",
366
+ unsupported_arg=argument.name
367
+ )
368
+
347
369
  # Add the arguments
348
370
  for argument in info.arguments.values():
349
371
  config: _Dict[str, _Any] = {
@@ -351,19 +373,21 @@ def _add_arguments(
351
373
  'dest': argument.name,
352
374
  'help': argument.help
353
375
  }
376
+ flags = generate_flag(argument, reserved_flags=reserved_flags)
354
377
  if argument.annotation is bool:
355
378
  config['action'] = 'store_true'
356
379
  elif argument.annotation:
357
380
  config['type'] = argument.annotation
358
381
  if argument.kind == _inspect._ParameterKind.POSITIONAL_ONLY:
359
- config['required'] = True
382
+ flags = [config.pop('dest')]
360
383
  if argument.kind == _inspect._ParameterKind.VAR_POSITIONAL:
361
384
  config['nargs'] = '*'
385
+ flags = [config.pop('dest')]
386
+ if argument.kind == _inspect._ParameterKind.POSITIONAL_OR_KEYWORD and keyword_only_exists:
387
+ config['required'] = True
362
388
  if argument.default is not None:
363
389
  config['default'] = argument.default
364
- parser.add_argument(
365
- *generate_flag(argument, reserved_flags=reserved_flags), **config
366
- )
390
+ parser.add_argument(*flags, **config)
367
391
 
368
392
 
369
393
  def _argumentify_one(func: Callable) -> None:
@@ -373,16 +397,6 @@ def _argumentify_one(func: Callable) -> None:
373
397
  """
374
398
  info = FunctionInfo(func)
375
399
 
376
- # Check if the function has a VAR_KEYWORD argument
377
- # Raises a ArgumentTypeError if it does
378
- for argument in info.arguments.values():
379
- if argument.kind == _inspect._ParameterKind.VAR_KEYWORD:
380
- raise ArgumentTypeError(
381
- f"The function has a `**{argument.name}` argument, "
382
- "which is not supported.",
383
- unsupported_arg=argument.name
384
- )
385
-
386
400
  # Create the parser
387
401
  parser = _argparse.ColorizingArgumentParser(description=info.docstring.description)
388
402
  # Add the arguments
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes