log21 2.9.1__tar.gz → 2.10.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.
Files changed (27) hide show
  1. {log21-2.9.1/src/log21.egg-info → log21-2.10.0}/PKG-INFO +5 -3
  2. {log21-2.9.1 → log21-2.10.0}/README.md +4 -2
  3. {log21-2.9.1 → log21-2.10.0}/pyproject.toml +17 -17
  4. {log21-2.9.1 → log21-2.10.0}/src/log21/Argparse.py +44 -28
  5. {log21-2.9.1 → log21-2.10.0}/src/log21/Argumentify.py +107 -18
  6. {log21-2.9.1 → log21-2.10.0}/src/log21/__init__.py +5 -3
  7. {log21-2.9.1 → log21-2.10.0/src/log21.egg-info}/PKG-INFO +5 -3
  8. {log21-2.9.1 → log21-2.10.0}/LICENSE.txt +0 -0
  9. {log21-2.9.1 → log21-2.10.0}/setup.cfg +0 -0
  10. {log21-2.9.1 → log21-2.10.0}/src/log21/Colors.py +0 -0
  11. {log21-2.9.1 → log21-2.10.0}/src/log21/CrashReporter/Formatters.py +0 -0
  12. {log21-2.9.1 → log21-2.10.0}/src/log21/CrashReporter/Reporters.py +0 -0
  13. {log21-2.9.1 → log21-2.10.0}/src/log21/CrashReporter/__init__.py +0 -0
  14. {log21-2.9.1 → log21-2.10.0}/src/log21/FileHandler.py +0 -0
  15. {log21-2.9.1 → log21-2.10.0}/src/log21/Formatters.py +0 -0
  16. {log21-2.9.1 → log21-2.10.0}/src/log21/Levels.py +0 -0
  17. {log21-2.9.1 → log21-2.10.0}/src/log21/Logger.py +0 -0
  18. {log21-2.9.1 → log21-2.10.0}/src/log21/LoggingWindow.py +0 -0
  19. {log21-2.9.1 → log21-2.10.0}/src/log21/Manager.py +0 -0
  20. {log21-2.9.1 → log21-2.10.0}/src/log21/PPrint.py +0 -0
  21. {log21-2.9.1 → log21-2.10.0}/src/log21/ProgressBar.py +0 -0
  22. {log21-2.9.1 → log21-2.10.0}/src/log21/StreamHandler.py +0 -0
  23. {log21-2.9.1 → log21-2.10.0}/src/log21/TreePrint.py +0 -0
  24. {log21-2.9.1 → log21-2.10.0}/src/log21.egg-info/SOURCES.txt +0 -0
  25. {log21-2.9.1 → log21-2.10.0}/src/log21.egg-info/dependency_links.txt +0 -0
  26. {log21-2.9.1 → log21-2.10.0}/src/log21.egg-info/requires.txt +0 -0
  27. {log21-2.9.1 → log21-2.10.0}/src/log21.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: log21
3
- Version: 2.9.1
3
+ Version: 2.10.0
4
4
  Summary: A simple logging package that helps you log colorized messages in Windows console.
5
5
  Author-email: "CodeWriter21(Mehrad Pooryoussof)" <CodeWriter21@gmail.com>
6
6
  License: Apache License 2.0
@@ -92,9 +92,11 @@ pip install git+https://github.com/MPCodeWriter21/log21
92
92
  Changes
93
93
  -------
94
94
 
95
- ### 2.9.1
95
+ ### 2.10.0
96
96
 
97
- + Update `README.md`.
97
+ + Added some exception classes to raise in the "argumentified" functions to show
98
+ *parser error* to the user: `ArgumentError`, `IncompatibleArguments`,
99
+ `RequiredArgument`, `TooFewArguments`
98
100
 
99
101
  [Full CHANGELOG](https://github.com/MPCodeWriter21/log21/blob/master/CHANGELOG.md)
100
102
 
@@ -61,9 +61,11 @@ pip install git+https://github.com/MPCodeWriter21/log21
61
61
  Changes
62
62
  -------
63
63
 
64
- ### 2.9.1
64
+ ### 2.10.0
65
65
 
66
- + Update `README.md`.
66
+ + Added some exception classes to raise in the "argumentified" functions to show
67
+ *parser error* to the user: `ArgumentError`, `IncompatibleArguments`,
68
+ `RequiredArgument`, `TooFewArguments`
67
69
 
68
70
  [Full CHANGELOG](https://github.com/MPCodeWriter21/log21/blob/master/CHANGELOG.md)
69
71
 
@@ -5,7 +5,7 @@ build-backend = "setuptools.build_meta"
5
5
  [project]
6
6
  name = "log21"
7
7
  authors = [
8
- {name = "CodeWriter21(Mehrad Pooryoussof)", email = "CodeWriter21@gmail.com"}
8
+ {name = "CodeWriter21(Mehrad Pooryoussof)", email = "CodeWriter21@gmail.com"}
9
9
  ]
10
10
  description = "A simple logging package that helps you log colorized messages in Windows console."
11
11
  readme = {file = "README.md", content-type = "text/markdown"}
@@ -13,20 +13,20 @@ requires-python = ">=3.8"
13
13
  keywords = ['python', 'log', 'colorize', 'color', 'logging', 'Python3', 'CodeWriter21']
14
14
  license = {text = "Apache License 2.0"}
15
15
  classifiers = [
16
- "Intended Audience :: Developers",
17
- "Programming Language :: Python :: 3",
18
- "Programming Language :: Python :: 3.7",
19
- "Programming Language :: Python :: 3.10",
20
- "Programming Language :: Python :: 3.11",
21
- "Operating System :: Unix",
22
- "Operating System :: Microsoft :: Windows",
23
- "Operating System :: MacOS :: MacOS X"
16
+ "Intended Audience :: Developers",
17
+ "Programming Language :: Python :: 3",
18
+ "Programming Language :: Python :: 3.7",
19
+ "Programming Language :: Python :: 3.10",
20
+ "Programming Language :: Python :: 3.11",
21
+ "Operating System :: Unix",
22
+ "Operating System :: Microsoft :: Windows",
23
+ "Operating System :: MacOS :: MacOS X"
24
24
  ]
25
25
  dependencies = [
26
- "webcolors",
27
- "docstring-parser"
26
+ "webcolors",
27
+ "docstring-parser"
28
28
  ]
29
- version = "2.9.1"
29
+ version = "2.10.0"
30
30
 
31
31
  [tool.setuptools.packages.find]
32
32
  where = ["src"]
@@ -43,11 +43,11 @@ dev = ["yapf", "isort", "docformatter", "pylint", "json5", "pytest"]
43
43
  max-line-length = 88
44
44
 
45
45
  disable = [
46
- "too-few-public-methods",
47
- "too-many-arguments",
48
- "protected-access",
49
- "too-many-locals",
50
- "fixme",
46
+ "too-few-public-methods",
47
+ "too-many-arguments",
48
+ "protected-access",
49
+ "too-many-locals",
50
+ "fixme",
51
51
  ]
52
52
 
53
53
  [tool.pylint.design]
@@ -8,9 +8,10 @@ import sys as _sys
8
8
  import types as _types
9
9
  import typing as _typing
10
10
  import argparse as _argparse
11
+ import collections.abc
11
12
  from enum import Enum as _Enum
12
- from typing import (Tuple as _Tuple, Mapping as _Mapping, Optional as _Optional,
13
- Sequence as _Sequence)
13
+ from typing import (Tuple as _Tuple, Mapping as _Mapping, NoReturn,
14
+ Optional as _Optional, Sequence as _Sequence)
14
15
  from gettext import gettext as _gettext
15
16
  from textwrap import TextWrapper as _TextWrapper
16
17
  from collections import OrderedDict as _OrderedDict
@@ -78,7 +79,7 @@ class ColorizingHelpFormatter(_argparse.HelpFormatter):
78
79
  if key in self.colors:
79
80
  self.colors[key] = value
80
81
 
81
- class _Section(object):
82
+ class _Section:
82
83
 
83
84
  def __init__(self, formatter, parent, heading=None):
84
85
  self.formatter = formatter
@@ -561,10 +562,32 @@ class _ActionsContainer(_argparse._ActionsContainer):
561
562
  and isinstance(func_type, _typing._LiteralGenericAlias)): # type: ignore
562
563
  func_type = Literal(func_type)
563
564
 
565
+ # Handle `Union` and `Optional` as a type (e.g. `Union[int, str]` and
566
+ # `Optional[int]`)
567
+ elif (hasattr(_typing, '_UnionGenericAlias')
568
+ and isinstance(func_type, _typing._UnionGenericAlias)): # type: ignore
569
+ # Optional[T] is just Union[T, NoneType]
570
+ # Optional
571
+ if (hasattr(_types, 'NoneType') and len(func_type.__args__) == 2
572
+ and func_type.__args__[1] is _types.NoneType):
573
+ action.required = False
574
+ func_type = func_type.__args__[0]
575
+ # Union
576
+ else:
577
+ func_type = func_type.__args__ # type: ignore
578
+
564
579
  # Handle `List` as a type (e.g. `List[int]`)
565
580
  elif (hasattr(_typing, '_GenericAlias')
566
581
  and isinstance(func_type, _typing._GenericAlias) # type: ignore
567
- and func_type.__origin__ is list):
582
+ and getattr(func_type, '__origin__') is list):
583
+ func_type = func_type.__args__[0]
584
+ if kwargs.get('nargs') is None:
585
+ action.nargs = '+'
586
+
587
+ # Handle `Sequence` as a type (e.g. `Sequence[int]`)
588
+ elif (hasattr(_typing, '_GenericAlias')
589
+ and isinstance(func_type, _typing._GenericAlias) # type: ignore
590
+ and getattr(func_type, '__origin__') is collections.abc.Sequence):
568
591
  func_type = func_type.__args__[0]
569
592
  if kwargs.get('nargs') is None:
570
593
  action.nargs = '+'
@@ -572,25 +595,10 @@ class _ActionsContainer(_argparse._ActionsContainer):
572
595
  # Handle `Required` as a type (e.g. `Required[int]`)
573
596
  elif (hasattr(_typing, 'Required') and hasattr(_typing, '_GenericAlias')
574
597
  and isinstance(func_type, _typing._GenericAlias) # type: ignore
575
- and func_type.__origin__ is _typing.Required
576
- ):
598
+ and getattr(func_type, '__origin__') is _typing.Required):
577
599
  func_type = func_type.__args__[0]
578
600
  action.required = True
579
601
 
580
- # Handle `Union` and `Optional` as a type (e.g. `Union[int, str]` and
581
- # `Optional[int]`)
582
- elif (hasattr(_types, 'NoneType') and hasattr(_typing, '_UnionGenericAlias')
583
- and isinstance(func_type, _typing._UnionGenericAlias)): # type: ignore
584
- # Optional[T] is just Union[T, NoneType]
585
- # Optional
586
- if (len(func_type.__args__) == 2
587
- and func_type.__args__[1] is _types.NoneType):
588
- action.required = False
589
- func_type = func_type.__args__[0]
590
- # Union
591
- else:
592
- func_type = func_type.__args__ # type: ignore
593
-
594
602
  # Handle Enum as a type
595
603
  elif callable(func_type) and isinstance(func_type, type) and issubclass(
596
604
  func_type, _Enum) and action.choices is None and level == 0:
@@ -747,16 +755,15 @@ class ColorizingArgumentParser(_argparse.ArgumentParser, _ActionsContainer):
747
755
  f'%(prog)s: {_gc("r")}error{_gc("lr")}:{_gc("rst")} %(message)s\n'
748
756
  ) % args
749
757
  )
758
+ return NoReturn
750
759
 
751
760
  def _get_formatter(self):
752
761
  if hasattr(self.formatter_class, 'colors'):
753
762
  return self.formatter_class(prog=self.prog, colors=self.colors)
754
- else:
755
- return self.formatter_class(prog=self.prog)
763
+ return self.formatter_class(prog=self.prog)
756
764
 
757
765
  def _get_value(self, action, arg_string):
758
- """Override _get_value to add support for types such as Union and
759
- Literal."""
766
+ """Override _get_value to add support for types such as Union and Literal."""
760
767
 
761
768
  func_type = self._registry_get('type', action.type, action.type)
762
769
  if not callable(func_type) and not isinstance(func_type, tuple):
@@ -842,13 +849,14 @@ class ColorizingArgumentParser(_argparse.ArgumentParser, _ActionsContainer):
842
849
  for arg_string in arg_strings:
843
850
 
844
851
  # for regular arguments, just add them back into the list
845
- if not arg_string or arg_string[0] not in self.fromfile_prefix_chars:
852
+ if not arg_string or arg_string[0] not in (self.fromfile_prefix_chars
853
+ or ''):
846
854
  new_arg_strings.append(arg_string)
847
855
 
848
856
  # replace arguments referencing files with the file content
849
857
  else:
850
858
  try:
851
- with open(arg_string[1:]) as args_file:
859
+ with open(arg_string[1:], encoding='utf-8') as args_file:
852
860
  arg_strings = []
853
861
  for arg_line in args_file.read().splitlines():
854
862
  for arg in self.convert_arg_line_to_args(arg_line):
@@ -1110,7 +1118,11 @@ class ColorizingArgumentParser(_argparse.ArgumentParser, _ActionsContainer):
1110
1118
  if action.help is not _argparse.SUPPRESS
1111
1119
  ]
1112
1120
  self.error(
1113
- _gettext(f'one of the arguments {" ".join(names)} is required')
1121
+ _gettext(
1122
+ 'one of the arguments ' +
1123
+ ' '.join(name for name in names if name is not None) +
1124
+ ' is required'
1125
+ )
1114
1126
  )
1115
1127
 
1116
1128
  for group in self._action_groups:
@@ -1127,7 +1139,11 @@ class ColorizingArgumentParser(_argparse.ArgumentParser, _ActionsContainer):
1127
1139
  if action.help is not _argparse.SUPPRESS
1128
1140
  ]
1129
1141
  self.error(
1130
- _gettext(f'one of the arguments {" ".join(names)} is required')
1142
+ _gettext(
1143
+ 'one of the arguments ' +
1144
+ ' '.join(name for name in names if name is not None) +
1145
+ ' is required'
1146
+ )
1131
1147
  )
1132
1148
 
1133
1149
  # return the updated namespace and the extra arguments
@@ -18,7 +18,8 @@ import log21.Argparse as _Argparse
18
18
  __all__ = [
19
19
  'argumentify', 'ArgumentifyError', 'ArgumentTypeError', 'FlagGenerationError',
20
20
  'RESERVED_FLAGS', 'Callable', 'Argument', 'FunctionInfo', 'generate_flag',
21
- 'normalize_name', 'normalize_name_to_snake_case'
21
+ 'normalize_name', 'normalize_name_to_snake_case', 'ArgumentError',
22
+ 'IncompatibleArguments', 'RequiredArgument', 'TooFewArguments'
22
23
  ]
23
24
 
24
25
  Callable = _Union[_Callable[..., _Any], _Callable[..., _Coroutine[_Any, _Any, _Any]]]
@@ -76,6 +77,90 @@ class FlagGenerationError(ArgumentifyError, RuntimeError):
76
77
  self.arg_name = arg_name
77
78
 
78
79
 
80
+ class ArgumentError(ArgumentifyError):
81
+ """Base of errors to raise in the argumentified functions to raise parser errors."""
82
+
83
+ def __init__(self, *args, message: _Optional[str] = None):
84
+ """Initialize the exception.
85
+
86
+ :param args: The arguments that have a problem.
87
+ :param message: The error message to show.
88
+ """
89
+ if message is None:
90
+ if args:
91
+ if len(args) > 1:
92
+ message = "There is a problem with the arguments: " + ', '.join(
93
+ f"`{arg}`" for arg in args
94
+ )
95
+ else:
96
+ message = "The argument `" + args[0] + "` is invalid."
97
+ self.message = message
98
+ self.arguments = args
99
+
100
+
101
+ class IncompatibleArguments(ArgumentError):
102
+ """Raise when the user has used arguments that are incompatible with each other."""
103
+
104
+ def __init__(self, *args, message: _Optional[str] = None):
105
+ """Initialize the exception.
106
+
107
+ :param args: The arguments that are incompatible.
108
+ :param message: The error message to show.
109
+ """
110
+ super().__init__(*args, message)
111
+ if message is None:
112
+ if args:
113
+ if len(args) > 1:
114
+ message = "You cannot use all these together: " + ', '.join(
115
+ f"`{arg}`" for arg in args
116
+ )
117
+ else:
118
+ message = "The argument `" + args[0] + "` is not compatible."
119
+ self.message = message
120
+
121
+
122
+ class RequiredArgument(ArgumentError):
123
+ """Raise this when there is a required argument missing."""
124
+
125
+ def __init__(self, *args, message: _Optional[str] = None):
126
+ """Initialize the exception.
127
+
128
+ :param args: The arguments that are required.
129
+ :param message: The error message to show.
130
+ """
131
+ super().__init__(*args, message)
132
+ if message is None:
133
+ if args:
134
+ if len(args) > 1:
135
+ message = "These arguments are required: " + ', '.join(
136
+ f"`{arg}`" for arg in args
137
+ )
138
+ else:
139
+ message = "The argument `" + args[0] + "` is required."
140
+ self.message = message
141
+
142
+
143
+ class TooFewArguments(ArgumentError):
144
+ """Raise this when there were not enough arguments passed."""
145
+
146
+ def __init__(self, *args, message: _Optional[str] = None):
147
+ """Initialize the exception.
148
+
149
+ :param args: The arguments that should be passed.
150
+ :param message: The error message to show.
151
+ """
152
+ super().__init__(*args, message)
153
+ if message is None:
154
+ if args:
155
+ if len(args) > 1:
156
+ message = "You should use these arguments: " + ', '.join(
157
+ f"`{arg}`" for arg in args
158
+ )
159
+ else:
160
+ message = "The argument `" + args[0] + "` should be used."
161
+ self.message = message
162
+
163
+
79
164
  def normalize_name_to_snake_case(name: str, sep_char: str = '_') -> str:
80
165
  """Returns the normalized name a class.
81
166
 
@@ -262,11 +347,10 @@ def _add_arguments(
262
347
  'dest': argument.name,
263
348
  'help': argument.help
264
349
  }
265
- if isinstance(argument.annotation, type):
266
- config['type'] = argument.annotation
267
350
  if argument.annotation == bool:
268
351
  config['action'] = 'store_true'
269
- config.pop('type')
352
+ elif argument.annotation:
353
+ config['type'] = argument.annotation
270
354
  if argument.kind == _inspect._ParameterKind.POSITIONAL_ONLY:
271
355
  config['required'] = True
272
356
  if argument.kind == _inspect._ParameterKind.VAR_POSITIONAL:
@@ -279,8 +363,7 @@ def _add_arguments(
279
363
 
280
364
 
281
365
  def _argumentify_one(func: Callable):
282
- """This function argumentifies one function as the entry point of the
283
- script.
366
+ """This function argumentifies one function as the entry point of the script.
284
367
 
285
368
  :param function: The function to argumentify.
286
369
  """
@@ -313,15 +396,18 @@ def _argumentify_one(func: Callable):
313
396
  args.extend(getattr(cli_args, argument.name) or [])
314
397
  else:
315
398
  kwargs[argument.name] = getattr(cli_args, argument.name)
316
- result = func(*args, **kwargs)
317
- # Check if the result is a coroutine
318
- if isinstance(result, (_Coroutine, _Awaitable)):
319
- _asyncio.run(result)
399
+ try:
400
+ result = func(*args, **kwargs)
401
+ # Check if the result is a coroutine
402
+ if isinstance(result, (_Coroutine, _Awaitable)):
403
+ _asyncio.run(result)
404
+ except ArgumentError as error:
405
+ parser.error(error.message)
320
406
 
321
407
 
322
408
  def _argumentify(functions: _Dict[str, Callable]):
323
- """This function argumentifies one or more functions as the entry point of
324
- the script.
409
+ """This function argumentifies one or more functions as the entry point of the
410
+ script.
325
411
 
326
412
  :param functions: A dictionary of functions to argumentify.
327
413
  :raises RuntimeError:
@@ -362,15 +448,18 @@ def _argumentify(functions: _Dict[str, Callable]):
362
448
  args.extend(getattr(cli_args, argument.name) or [])
363
449
  else:
364
450
  kwargs[argument.name] = getattr(cli_args, argument.name)
365
- result = function(*args, **kwargs)
366
- # Check if the result is a coroutine
367
- if isinstance(result, (_Coroutine, _Awaitable)):
368
- _asyncio.run(result)
451
+ try:
452
+ result = function(*args, **kwargs)
453
+ # Check if the result is a coroutine
454
+ if isinstance(result, (_Coroutine, _Awaitable)):
455
+ _asyncio.run(result)
456
+ except ArgumentError as error:
457
+ parser.error(error.message)
369
458
 
370
459
 
371
460
  def argumentify(entry_point: _Union[Callable, _List[Callable], _Dict[str, Callable]]):
372
- """This function argumentifies one or more functions as the entry point of
373
- the script.
461
+ """This function argumentifies one or more functions as the entry point of the
462
+ script.
374
463
 
375
464
  1 #!/usr/bin/env python
376
465
  2 # argumentified.py
@@ -16,14 +16,15 @@ from .Manager import Manager
16
16
  from .Argparse import ColorizingArgumentParser
17
17
  from .TreePrint import TreePrint, tree_format
18
18
  from .Formatters import ColorizingFormatter, DecolorizingFormatter, _Formatter
19
- from .Argumentify import argumentify
19
+ from .Argumentify import (ArgumentError, TooFewArguments, RequiredArgument,
20
+ IncompatibleArguments, argumentify)
20
21
  from .FileHandler import FileHandler, DecolorizingFileHandler
21
22
  from .ProgressBar import ProgressBar
22
23
  from .LoggingWindow import LoggingWindow, LoggingWindowHandler
23
24
  from .StreamHandler import StreamHandler, ColorizingStreamHandler
24
25
 
25
26
  __author__ = 'CodeWriter21 (Mehrad Pooryoussof)'
26
- __version__ = '2.9.1'
27
+ __version__ = '2.10.0'
27
28
  __github__ = 'Https://GitHub.com/MPCodeWriter21/log21'
28
29
  __all__ = [
29
30
  'ColorizingStreamHandler', 'DecolorizingFileHandler', 'ColorizingFormatter',
@@ -35,7 +36,8 @@ __all__ = [
35
36
  'debug', 'info', 'warning', 'warn', 'error', 'critical', 'fatal', 'exception',
36
37
  'log', 'basic_config', 'basicConfig', 'ProgressBar', 'progress_bar',
37
38
  'LoggingWindow', 'LoggingWindowHandler', 'get_logging_window', 'CrashReporter',
38
- 'console_reporter', 'file_reporter', 'argumentify'
39
+ 'console_reporter', 'file_reporter', 'argumentify', 'ArgumentError',
40
+ 'IncompatibleArguments', 'RequiredArgument', 'TooFewArguments'
39
41
  ]
40
42
 
41
43
  _manager = Manager()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: log21
3
- Version: 2.9.1
3
+ Version: 2.10.0
4
4
  Summary: A simple logging package that helps you log colorized messages in Windows console.
5
5
  Author-email: "CodeWriter21(Mehrad Pooryoussof)" <CodeWriter21@gmail.com>
6
6
  License: Apache License 2.0
@@ -92,9 +92,11 @@ pip install git+https://github.com/MPCodeWriter21/log21
92
92
  Changes
93
93
  -------
94
94
 
95
- ### 2.9.1
95
+ ### 2.10.0
96
96
 
97
- + Update `README.md`.
97
+ + Added some exception classes to raise in the "argumentified" functions to show
98
+ *parser error* to the user: `ArgumentError`, `IncompatibleArguments`,
99
+ `RequiredArgument`, `TooFewArguments`
98
100
 
99
101
  [Full CHANGELOG](https://github.com/MPCodeWriter21/log21/blob/master/CHANGELOG.md)
100
102
 
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