Skip to content

Commit

Permalink
New exception classes to use with argumentify!
Browse files Browse the repository at this point in the history
  • Loading branch information
MPCodeWriter21 committed May 13, 2024
1 parent 130bb8b commit 5e13bcd
Show file tree
Hide file tree
Showing 7 changed files with 190 additions and 38 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ Help this project by [Donation](DONATE.md)
Changes
-------

### 2.10.0

+ Added some exception classes to raise in the "argumentified" functions to show
*parser error* to the user: `ArgumentError`, `IncompatibleArguments`,
`RequiredArgument`, `TooFewArguments`

### 2.9.2

+ Added `Sequence[T]` as a supported type to the ColorizingArgumentParser.
Expand Down
53 changes: 53 additions & 0 deletions EXAMPLES.md
Original file line number Diff line number Diff line change
Expand Up @@ -376,3 +376,56 @@ if __name__ == "__main__":
![multi-entry](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-6.3.png)


Example with parser errors:

```python
# Common Section
import log21


class ReversedText:
def __init__(self, text: str):
self._text = text[::-1]

def __str__(self):
return self._text

def __repr__(self):
return f"<{self.__class__.__name__}(text='{self._text}') at {hex(id(self))}>"


# New way
def main(positional_arg: int, /, optional_arg: ReversedText, arg_with_default: int = 21,
additional_arg=None, verbose: bool = False, quiet: bool = False):
"""Some description
:param positional_arg: This argument is positional!
:param optional_arg: Whatever you pass here will be REVERSED!
:param arg_with_default: The default value is 21
:param additional_arg: This one is extra.
:param verbose: Increase verbosity
:param quiet: Make the script quiet
"""
if verbose and quiet:
raise log21.IncompatibleArguments(
'--verbose',
'--quiet',
message="You can not make the script quiet and except it to be more verbose!"
)
if verbose:
log21.basic_config(level='DEBUG')
if quiet:
log21.basic_config(level='WARN')

log21.info(f"{positional_arg = }")
log21.info(f"{optional_arg = !s}")
log21.warn("THIS IS IMPORTANT!!!")
log21.debug(f"{arg_with_default = }")
log21.debug(f"{additional_arg = !s}")


if __name__ == '__main__':
log21.argumentify(main)
```

![parser-error](https://github.com/MPCodeWriter21/log21/raw/master/screen-shots/example-6.4.png)
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,11 @@ pip install git+https://github.com/MPCodeWriter21/log21
Changes
-------

### 2.9.2
### 2.10.0

+ Added `Sequence[T]` as a supported type to the ColorizingArgumentParser.
+ Bug fixes.
+ Added some exception classes to raise in the "argumentified" functions to show
*parser error* to the user: `ArgumentError`, `IncompatibleArguments`,
`RequiredArgument`, `TooFewArguments`

[Full CHANGELOG](https://github.com/MPCodeWriter21/log21/blob/master/CHANGELOG.md)

Expand Down
34 changes: 17 additions & 17 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,28 @@ build-backend = "setuptools.build_meta"
[project]
name = "log21"
authors = [
{name = "CodeWriter21(Mehrad Pooryoussof)", email = "[email protected]"}
{name = "CodeWriter21(Mehrad Pooryoussof)", email = "[email protected]"}
]
description = "A simple logging package that helps you log colorized messages in Windows console."
readme = {file = "README.md", content-type = "text/markdown"}
requires-python = ">=3.8"
keywords = ['python', 'log', 'colorize', 'color', 'logging', 'Python3', 'CodeWriter21']
license = {text = "Apache License 2.0"}
classifiers = [
"Intended Audience :: Developers",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Operating System :: Unix",
"Operating System :: Microsoft :: Windows",
"Operating System :: MacOS :: MacOS X"
"Intended Audience :: Developers",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Operating System :: Unix",
"Operating System :: Microsoft :: Windows",
"Operating System :: MacOS :: MacOS X"
]
dependencies = [
"webcolors",
"docstring-parser"
"webcolors",
"docstring-parser"
]
version = "2.9.2"
version = "2.10.0"

[tool.setuptools.packages.find]
where = ["src"]
Expand All @@ -43,11 +43,11 @@ dev = ["yapf", "isort", "docformatter", "pylint", "json5", "pytest"]
max-line-length = 88

disable = [
"too-few-public-methods",
"too-many-arguments",
"protected-access",
"too-many-locals",
"fixme",
"too-few-public-methods",
"too-many-arguments",
"protected-access",
"too-many-locals",
"fixme",
]

[tool.pylint.design]
Expand Down
Binary file added screen-shots/example-6.4.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
120 changes: 105 additions & 15 deletions src/log21/Argumentify.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@
__all__ = [
'argumentify', 'ArgumentifyError', 'ArgumentTypeError', 'FlagGenerationError',
'RESERVED_FLAGS', 'Callable', 'Argument', 'FunctionInfo', 'generate_flag',
'normalize_name', 'normalize_name_to_snake_case'
'normalize_name', 'normalize_name_to_snake_case', 'ArgumentError',
'IncompatibleArguments', 'RequiredArgument', 'TooFewArguments'
]

Callable = _Union[_Callable[..., _Any], _Callable[..., _Coroutine[_Any, _Any, _Any]]]
Expand Down Expand Up @@ -76,6 +77,90 @@ def __init__(self, message: _Optional[str] = None, arg_name: _Optional[str] = No
self.arg_name = arg_name


class ArgumentError(ArgumentifyError):
"""Base of errors to raise in the argumentified functions to raise parser errors."""

def __init__(self, *args, message: _Optional[str] = None):
"""Initialize the exception.
:param args: The arguments that have a problem.
:param message: The error message to show.
"""
if message is None:
if args:
if len(args) > 1:
message = "There is a problem with the arguments: " + ', '.join(
f"`{arg}`" for arg in args
)
else:
message = "The argument `" + args[0] + "` is invalid."
self.message = message
self.arguments = args


class IncompatibleArguments(ArgumentError):
"""Raise when the user has used arguments that are incompatible with each other."""

def __init__(self, *args, message: _Optional[str] = None):
"""Initialize the exception.
:param args: The arguments that are incompatible.
:param message: The error message to show.
"""
super().__init__(*args, message)
if message is None:
if args:
if len(args) > 1:
message = "You cannot use all these together: " + ', '.join(
f"`{arg}`" for arg in args
)
else:
message = "The argument `" + args[0] + "` is not compatible."
self.message = message


class RequiredArgument(ArgumentError):
"""Raise this when there is a required argument missing."""

def __init__(self, *args, message: _Optional[str] = None):
"""Initialize the exception.
:param args: The arguments that are required.
:param message: The error message to show.
"""
super().__init__(*args, message)
if message is None:
if args:
if len(args) > 1:
message = "These arguments are required: " + ', '.join(
f"`{arg}`" for arg in args
)
else:
message = "The argument `" + args[0] + "` is required."
self.message = message


class TooFewArguments(ArgumentError):
"""Raise this when there were not enough arguments passed."""

def __init__(self, *args, message: _Optional[str] = None):
"""Initialize the exception.
:param args: The arguments that should be passed.
:param message: The error message to show.
"""
super().__init__(*args, message)
if message is None:
if args:
if len(args) > 1:
message = "You should use these arguments: " + ', '.join(
f"`{arg}`" for arg in args
)
else:
message = "The argument `" + args[0] + "` should be used."
self.message = message


def normalize_name_to_snake_case(name: str, sep_char: str = '_') -> str:
"""Returns the normalized name a class.
Expand Down Expand Up @@ -278,8 +363,7 @@ def _add_arguments(


def _argumentify_one(func: Callable):
"""This function argumentifies one function as the entry point of the
script.
"""This function argumentifies one function as the entry point of the script.
:param function: The function to argumentify.
"""
Expand Down Expand Up @@ -312,15 +396,18 @@ def _argumentify_one(func: Callable):
args.extend(getattr(cli_args, argument.name) or [])
else:
kwargs[argument.name] = getattr(cli_args, argument.name)
result = func(*args, **kwargs)
# Check if the result is a coroutine
if isinstance(result, (_Coroutine, _Awaitable)):
_asyncio.run(result)
try:
result = func(*args, **kwargs)
# Check if the result is a coroutine
if isinstance(result, (_Coroutine, _Awaitable)):
_asyncio.run(result)
except ArgumentError as error:
parser.error(error.message)


def _argumentify(functions: _Dict[str, Callable]):
"""This function argumentifies one or more functions as the entry point of
the script.
"""This function argumentifies one or more functions as the entry point of the
script.
:param functions: A dictionary of functions to argumentify.
:raises RuntimeError:
Expand Down Expand Up @@ -361,15 +448,18 @@ def _argumentify(functions: _Dict[str, Callable]):
args.extend(getattr(cli_args, argument.name) or [])
else:
kwargs[argument.name] = getattr(cli_args, argument.name)
result = function(*args, **kwargs)
# Check if the result is a coroutine
if isinstance(result, (_Coroutine, _Awaitable)):
_asyncio.run(result)
try:
result = function(*args, **kwargs)
# Check if the result is a coroutine
if isinstance(result, (_Coroutine, _Awaitable)):
_asyncio.run(result)
except ArgumentError as error:
parser.error(error.message)


def argumentify(entry_point: _Union[Callable, _List[Callable], _Dict[str, Callable]]):
"""This function argumentifies one or more functions as the entry point of
the script.
"""This function argumentifies one or more functions as the entry point of the
script.
1 #!/usr/bin/env python
2 # argumentified.py
Expand Down
8 changes: 5 additions & 3 deletions src/log21/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@
from .Argparse import ColorizingArgumentParser
from .TreePrint import TreePrint, tree_format
from .Formatters import ColorizingFormatter, DecolorizingFormatter, _Formatter
from .Argumentify import argumentify
from .Argumentify import (ArgumentError, TooFewArguments, RequiredArgument,
IncompatibleArguments, argumentify)
from .FileHandler import FileHandler, DecolorizingFileHandler
from .ProgressBar import ProgressBar
from .LoggingWindow import LoggingWindow, LoggingWindowHandler
from .StreamHandler import StreamHandler, ColorizingStreamHandler

__author__ = 'CodeWriter21 (Mehrad Pooryoussof)'
__version__ = '2.9.2'
__version__ = '2.10.0'
__github__ = 'Https://GitHub.com/MPCodeWriter21/log21'
__all__ = [
'ColorizingStreamHandler', 'DecolorizingFileHandler', 'ColorizingFormatter',
Expand All @@ -35,7 +36,8 @@
'debug', 'info', 'warning', 'warn', 'error', 'critical', 'fatal', 'exception',
'log', 'basic_config', 'basicConfig', 'ProgressBar', 'progress_bar',
'LoggingWindow', 'LoggingWindowHandler', 'get_logging_window', 'CrashReporter',
'console_reporter', 'file_reporter', 'argumentify'
'console_reporter', 'file_reporter', 'argumentify', 'ArgumentError',
'IncompatibleArguments', 'RequiredArgument', 'TooFewArguments'
]

_manager = Manager()
Expand Down

0 comments on commit 5e13bcd

Please sign in to comment.