feat: impl docs (#4)

The entire public api is covered with documentation in two languages - Russian and English.

the library now supports the latest three versions of python - 3.12, 3.13 and 3.14

minor design changes: now, when a Boolean flag is entered, its value is an empty string, not None.

tests have been adapted to the supported versions of python, readmi has been redesigned in two languages, German is no longer available.
This commit is contained in:
kolo
2025-12-04 21:55:19 +03:00
committed by GitHub
parent a2ac6a608f
commit ce7e24b924
210 changed files with 13770 additions and 1183 deletions
+2 -4
View File
@@ -1,4 +1,2 @@
__all__ = ["ArgParser", "Orchestrator"]
from argenta.orchestrator.argparser.entity import ArgParser
from argenta.orchestrator.entity import Orchestrator
from argenta.orchestrator.argparser.entity import ArgParser as ArgParser
from argenta.orchestrator.entity import Orchestrator as Orchestrator
@@ -1,9 +1,4 @@
__all__ = [
"ArgParser",
"BooleanArgument",
"ValueArgument"
]
from argenta.orchestrator.argparser.entity import ArgParser
from argenta.orchestrator.argparser.arguments import BooleanArgument, ValueArgument
from argenta.orchestrator.argparser.arguments import BooleanArgument as BooleanArgument
from argenta.orchestrator.argparser.arguments import ValueArgument as ValueArgument
from argenta.orchestrator.argparser.entity import ArgParser as ArgParser
from argenta.orchestrator.argparser.entity import ArgSpace as ArgSpace
@@ -1,8 +1,3 @@
__all__ = ["BooleanArgument", "ValueArgument", "InputArgument"]
from argenta.orchestrator.argparser.arguments.models import (
BooleanArgument,
ValueArgument,
InputArgument
)
from argenta.orchestrator.argparser.arguments.models import BooleanArgument as BooleanArgument
from argenta.orchestrator.argparser.arguments.models import InputArgument as InputArgument
from argenta.orchestrator.argparser.arguments.models import ValueArgument as ValueArgument
@@ -1,3 +1,5 @@
__all__ = ["BooleanArgument", "ValueArgument", "InputArgument"]
from typing import Literal
@@ -5,10 +7,8 @@ class BaseArgument:
"""
Private. Base class for all arguments
"""
def __init__(self, name: str, *,
help: str,
is_deprecated: bool,
prefix: Literal["-", "--", "---"]):
def __init__(self, name: str, *, help: str, is_deprecated: bool, prefix: Literal["-", "--", "---"]):
"""
Public. Boolean argument, does not require a value
:param name: name of the argument
@@ -20,20 +20,24 @@ class BaseArgument:
self.help: str = help
self.is_deprecated: bool = is_deprecated
self.prefix: Literal["-", "--", "---"] = prefix
@property
def string_entity(self) -> str:
return self.prefix + self.name
class ValueArgument(BaseArgument):
def __init__(self, name: str, *,
prefix: Literal["-", "--", "---"] = "--",
help: str = "Help message for the value argument",
possible_values: list[str] | None = None,
default: str | None = None,
is_required: bool = False,
is_deprecated: bool = False):
def __init__(
self,
name: str,
*,
prefix: Literal["-", "--", "---"] = "--",
help: str = "Help message for the value argument",
possible_values: list[str] | None = None,
default: str | None = None,
is_required: bool = False,
is_deprecated: bool = False,
):
"""
Public. Value argument, must have the value
:param name: name of the argument
@@ -52,10 +56,14 @@ class ValueArgument(BaseArgument):
class BooleanArgument(BaseArgument):
def __init__(self, name: str, *,
prefix: Literal["-", "--", "---"] = "--",
help: str = "Help message for the boolean argument",
is_deprecated: bool = False):
def __init__(
self,
name: str,
*,
prefix: Literal["-", "--", "---"] = "--",
help: str = "Help message for the boolean argument",
is_deprecated: bool = False,
):
"""
Public. Boolean argument, does not require a value
:param name: name of the argument
@@ -68,15 +76,13 @@ class BooleanArgument(BaseArgument):
class InputArgument:
def __init__(self, name: str,
value: str | None,
founder_class: type[BaseArgument]) -> None:
def __init__(self, name: str, value: str | Literal[True], founder_class: type[BaseArgument]) -> None:
self.name: str = name
self.value: str | None = value
self.value: str | Literal[True] = value
self.founder_class: type[BaseArgument] = founder_class
def __str__(self) -> str:
return f"InputArgument({self.name}={self.value})"
def __repr__(self) -> str:
return f"InputArgument<name={self.name}, value={self.value}, founder_class={self.founder_class.__name__}>"
+98 -42
View File
@@ -1,3 +1,9 @@
__all__ = [
"ArgSpace",
"ArgParser",
]
import sys
from argparse import ArgumentParser, Namespace
from typing import Never, Self
@@ -5,40 +11,63 @@ from argenta.orchestrator.argparser.arguments.models import (
BaseArgument,
BooleanArgument,
InputArgument,
ValueArgument
ValueArgument,
)
class ArgSpace:
def __init__(self, all_arguments: list[InputArgument]) -> None:
self.all_arguments = all_arguments
@classmethod
def from_namespace(cls, namespace: Namespace,
processed_args: list[ValueArgument | BooleanArgument]) -> Self:
name_type_paired_args: dict[str, type[BaseArgument]] = {
arg.name: type(arg)
for arg in processed_args
self._name_object_paired_args: dict[str, InputArgument] = {}
self._type_object_paired_args: dict[type[BaseArgument], list[InputArgument]] = {
BooleanArgument: [],
ValueArgument: []
}
return cls([InputArgument(name=name,
value=value,
founder_class=name_type_paired_args[name])
for name, value in vars(namespace).items()])
self._setup_getters()
@classmethod
def from_namespace(
cls,
namespace: Namespace,
processed_args: list[ValueArgument | BooleanArgument]
) -> Self:
name_type_paired_processed_args: dict[str, type[BaseArgument]] = {
arg.name: type(arg) for arg in processed_args
}
parsed_arguments: list[InputArgument] = []
for name, value in vars(namespace).items():
parsed_arguments.append(
InputArgument(
name=name,
value=value,
founder_class=name_type_paired_processed_args[name]
)
)
return cls(parsed_arguments)
def _setup_getters(self) -> None:
if not self.all_arguments:
return
for input_arg in self.all_arguments:
self._name_object_paired_args[input_arg.name] = input_arg
self._type_object_paired_args[input_arg.founder_class].append(input_arg)
def get_by_name(self, name: str) -> InputArgument | None:
for arg in self.all_arguments:
if arg.name == name:
return arg
return None
return self._name_object_paired_args.get(name)
def get_by_type(self, arg_type: type[BaseArgument]) -> list[InputArgument] | list[Never]:
return [arg for arg in self.all_arguments if arg.founder_class is arg_type]
return self._type_object_paired_args.get(arg_type, [])
class ArgParser:
def __init__(
self,
processed_args: list[ValueArgument | BooleanArgument], *,
processed_args: list[ValueArgument | BooleanArgument],
*,
name: str = "Argenta",
description: str = "Argenta available arguments",
epilog: str = "github.com/koloideal/Argenta | made by kolo",
@@ -55,24 +84,51 @@ class ArgParser:
self.epilog: str = epilog
self.processed_args: list[ValueArgument | BooleanArgument] = processed_args
self._core: ArgumentParser = ArgumentParser(prog=name, description=description, epilog=epilog)
for arg in processed_args:
if isinstance(arg, BooleanArgument):
_ = self._core.add_argument(arg.string_entity,
action=arg.action,
help=arg.help,
deprecated=arg.is_deprecated)
else:
_ = self._core.add_argument(arg.string_entity,
action=arg.action,
help=arg.help,
default=arg.default,
choices=arg.possible_values,
required=arg.is_required,
deprecated=arg.is_deprecated)
self.parsed_argspace: ArgSpace = ArgSpace([])
def parse_args(self) -> ArgSpace:
return ArgSpace.from_namespace(namespace=self._core.parse_args(),
processed_args=self.processed_args)
self._core: ArgumentParser = ArgumentParser(prog=name, description=description, epilog=epilog)
self._register_args(processed_args)
def _parse_args(self) -> None:
self.parsed_argspace = ArgSpace.from_namespace(
namespace=self._core.parse_args(), processed_args=self.processed_args
)
def _register_args(self, processed_args: list[ValueArgument | BooleanArgument]) -> None:
if sys.version_info >= (3, 13):
for arg in processed_args:
if isinstance(arg, BooleanArgument):
_ = self._core.add_argument(
arg.string_entity,
action=arg.action,
help=arg.help,
deprecated=arg.is_deprecated
)
else:
_ = self._core.add_argument(
arg.string_entity,
action=arg.action,
help=arg.help,
default=arg.default,
choices=arg.possible_values,
required=arg.is_required,
deprecated=arg.is_deprecated,
)
else:
for arg in processed_args:
if isinstance(arg, BooleanArgument):
_ = self._core.add_argument(
arg.string_entity,
action=arg.action,
help=arg.help,
)
else:
_ = self._core.add_argument(
arg.string_entity,
action=arg.action,
help=arg.help,
default=arg.default,
choices=arg.possible_values,
required=arg.is_required
)
+17 -12
View File
@@ -1,20 +1,22 @@
from argenta.app import App
from argenta.response import Response
from argenta.orchestrator.argparser import ArgParser
from argenta.di.integration import setup_dishka
from argenta.di.providers import SystemProvider
__all__ = ["Orchestrator"]
from dishka import Provider, make_container
from argenta.app import App
from argenta.di.integration import setup_dishka
from argenta.di.providers import SystemProvider
from argenta.orchestrator.argparser import ArgParser
DEFAULT_ARGPARSER: ArgParser = ArgParser(processed_args=[])
class Orchestrator:
def __init__(self, arg_parser: ArgParser = DEFAULT_ARGPARSER,
custom_providers: list[Provider] = [],
auto_inject_handlers: bool = True):
def __init__(
self,
arg_parser: ArgParser = DEFAULT_ARGPARSER,
custom_providers: list[Provider] = [],
auto_inject_handlers: bool = True,
):
"""
Public. An orchestrator and configurator that defines the behavior of an integrated system, one level higher than the App
:param arg_parser: Cmd argument parser and configurator at startup
@@ -24,14 +26,17 @@ class Orchestrator:
self._custom_providers: list[Provider] = custom_providers
self._auto_inject_handlers: bool = auto_inject_handlers
self._arg_parser._parse_args()
def start_polling(self, app: App) -> None:
"""
Public. Starting the user input processing cycle
:param app: a running application
:return: None
"""
container = make_container(SystemProvider(self._arg_parser), *self._custom_providers)
Response.patch_by_container(container)
setup_dishka(app, auto_inject=self._auto_inject_handlers)
container = make_container(
SystemProvider(), *self._custom_providers, context={ArgParser: self._arg_parser}
)
setup_dishka(app, container, auto_inject=self._auto_inject_handlers)
app.run_polling()