log21 3.0.2__tar.gz → 3.2.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.2.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,55 +94,24 @@ pip install git+https://github.com/MPCodeWriter21/log21
94
94
  Changelog
95
95
  ---------
96
96
 
97
- ### v3.0.2
97
+ ### v3.2.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
+ Add `file_mode` and `file_encoding` parameters to `get_logger` for finer control over
100
+ the way a simple logger handles files.
101
101
 
102
- + Example:
102
+ For even more control you can still define Logger, Handlers, and Formatters manually.
103
103
 
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
-
109
- And now we can talk more about the tool...
110
-
111
- :param verbose: This flag will make the logs more verbose!
112
- """
113
-
114
- argumentify(main)
115
- ```
116
-
117
- The way old versions would look:
118
-
119
- ```help
120
- usage: test.py [-h] [--verbose]
104
+ #### Example
121
105
 
122
- This is a very useful tool and I will describe it thoroughly. It is so good that
123
-
124
- options:
125
- -h, --help
126
- show this help message and exit
127
- --verbose, -v
128
- This flag will make the logs more verbose!
129
-
130
- ```
131
-
132
- Now at v3.0.2:
133
-
134
- ```help
135
- usage: test.py [-h] [--verbose]
136
-
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...
106
+ ```python
107
+ import log21
139
108
 
140
- options:
141
- -h, --help
142
- show this help message and exit
143
- --verbose, -v
144
- This flag will make the logs more verbose!
109
+ logger = log21.get_logger(
110
+ "My File Logger", show_level=False, show_time=True, file="myapp.log", file_mode="a",
111
+ file_encoding="utf-8"
112
+ )
145
113
 
114
+ logger.info("Hello World!")
146
115
  ```
147
116
 
148
117
  [Full CHANGELOG](https://github.com/MPCodeWriter21/log21/blob/master/CHANGELOG.md)
@@ -69,55 +69,24 @@ pip install git+https://github.com/MPCodeWriter21/log21
69
69
  Changelog
70
70
  ---------
71
71
 
72
- ### v3.0.2
72
+ ### v3.2.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
+ Add `file_mode` and `file_encoding` parameters to `get_logger` for finer control over
75
+ the way a simple logger handles files.
76
76
 
77
- + Example:
77
+ For even more control you can still define Logger, Handlers, and Formatters manually.
78
78
 
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
-
84
- And now we can talk more about the tool...
85
-
86
- :param verbose: This flag will make the logs more verbose!
87
- """
88
-
89
- argumentify(main)
90
- ```
91
-
92
- The way old versions would look:
93
-
94
- ```help
95
- usage: test.py [-h] [--verbose]
79
+ #### Example
96
80
 
97
- This is a very useful tool and I will describe it thoroughly. It is so good that
98
-
99
- options:
100
- -h, --help
101
- show this help message and exit
102
- --verbose, -v
103
- This flag will make the logs more verbose!
104
-
105
- ```
106
-
107
- Now at v3.0.2:
108
-
109
- ```help
110
- usage: test.py [-h] [--verbose]
111
-
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...
81
+ ```python
82
+ import log21
114
83
 
115
- options:
116
- -h, --help
117
- show this help message and exit
118
- --verbose, -v
119
- This flag will make the logs more verbose!
84
+ logger = log21.get_logger(
85
+ "My File Logger", show_level=False, show_time=True, file="myapp.log", file_mode="a",
86
+ file_encoding="utf-8"
87
+ )
120
88
 
89
+ logger.info("Hello World!")
121
90
  ```
122
91
 
123
92
  [Full CHANGELOG](https://github.com/MPCodeWriter21/log21/blob/master/CHANGELOG.md)
@@ -23,7 +23,7 @@ dependencies = [
23
23
  "webcolors",
24
24
  "docstring-parser"
25
25
  ]
26
- version = "3.0.2"
26
+ version = "3.2.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.2.0'
35
35
  __github__ = 'https://GitHub.com/MPCodeWriter21/log21'
36
36
  __all__ = [
37
37
  'ColorizingStreamHandler', 'DecolorizingFileHandler', 'ColorizingFormatter',
@@ -60,7 +60,8 @@ def _prepare_formatter(
60
60
  colorize_time_and_level: bool = True,
61
61
  level_names: _Optional[_Mapping[int, str]] = None,
62
62
  level_colors: _Optional[_Mapping[int, tuple[str, ...]]] = None,
63
- formatter_class: _Type[_logging.Formatter] = ColorizingFormatter
63
+ formatter_class: _Type[_logging.Formatter] = ColorizingFormatter,
64
+ prefix_carriage_return: bool = True,
64
65
  ) -> _logging.Formatter:
65
66
  # Prepares a formatting if the fmt was None
66
67
  if not fmt:
@@ -70,7 +71,8 @@ def _prepare_formatter(
70
71
  fmt = '[%(levelname)s] ' + fmt
71
72
  if show_time:
72
73
  fmt = '[%(asctime)s] ' + fmt
73
- fmt = '\r' + fmt
74
+ if prefix_carriage_return:
75
+ fmt = '\r' + fmt
74
76
 
75
77
  if level_colors and not issubclass(formatter_class, ColorizingFormatter):
76
78
  warning(
@@ -116,7 +118,10 @@ def get_logger(
116
118
  override: bool = False,
117
119
  level_names: _Optional[_Mapping[int, str]] = None,
118
120
  level_colors: _Optional[_Mapping[int, tuple[str, ...]]] = None,
119
- file: _Optional[_Union[_os.PathLike, str]] = None
121
+ # TODO: Rename file to file_path in one future update
122
+ file: _Optional[_Union[_os.PathLike, str]] = None,
123
+ file_mode: _Optional[str] = None,
124
+ file_encoding: _Optional[str] = None,
120
125
  ) -> Logger:
121
126
  """Returns a logging.Logger with colorizing support.
122
127
 
@@ -182,7 +187,9 @@ def get_logger(
182
187
  :param level_names: Mapping[int, str] = None: You can specify custom level names.
183
188
  :param level_colors: Mapping[int, Tuple[str, ...]] = None: You can specify custom
184
189
  level colors.
185
- :param file: Union[os.PathLike, str] = None: The file to log to
190
+ :param file: Union[os.PathLike, str] = None: The file path to log to
191
+ :param file_mode: str = None: The mode to open file at (Defaults to 'a')
192
+ :param file_encoding: str = None: The file encoding
186
193
  :return: log21.Logger
187
194
  """
188
195
  if not isinstance(name, str):
@@ -209,7 +216,9 @@ def get_logger(
209
216
  _manager.addLogger(name, logger)
210
217
 
211
218
  if file:
212
- file_handler = FileHandler(file)
219
+ file_handler = DecolorizingFileHandler(
220
+ file, mode=file_mode or 'a', encoding=file_encoding
221
+ )
213
222
  file_formatter = _prepare_formatter(
214
223
  fmt,
215
224
  style,
@@ -218,7 +227,8 @@ def get_logger(
218
227
  show_time,
219
228
  False,
220
229
  level_names,
221
- formatter_class=DecolorizingFormatter
230
+ formatter_class=DecolorizingFormatter,
231
+ prefix_carriage_return=False,
222
232
  )
223
233
  file_handler.setFormatter(file_formatter)
224
234
  logger.addHandler(file_handler)
@@ -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