diff --git a/docs/code_snippets/error_handling/snippet.py b/docs/code_snippets/error_handling/snippet.py
index 63988e2..a270faa 100644
--- a/docs/code_snippets/error_handling/snippet.py
+++ b/docs/code_snippets/error_handling/snippet.py
@@ -1,9 +1,7 @@
from argenta import App
-
def empty_command_handler():
print("Empty command handler called")
-
app: App = App()
app.set_empty_command_handler(empty_command_handler)
diff --git a/docs/code_snippets/error_handling/snippet2.py b/docs/code_snippets/error_handling/snippet2.py
index 08a7124..324ade7 100644
--- a/docs/code_snippets/error_handling/snippet2.py
+++ b/docs/code_snippets/error_handling/snippet2.py
@@ -1,9 +1,7 @@
from argenta import App
-
def incorrect_input_syntax_handler(raw_command: str):
print(f"Incorrect input syntax for command: {raw_command}")
-
app: App = App()
app.set_incorrect_input_syntax_handler(incorrect_input_syntax_handler)
diff --git a/docs/code_snippets/error_handling/snippet3.py b/docs/code_snippets/error_handling/snippet3.py
index b3a0f15..a5a1f61 100644
--- a/docs/code_snippets/error_handling/snippet3.py
+++ b/docs/code_snippets/error_handling/snippet3.py
@@ -1,9 +1,7 @@
from argenta import App
-
def repeated_input_flags_handler(raw_command: str):
print(f"Repeated input flags: {raw_command}")
-
app: App = App()
app.set_repeated_input_flags_handler(repeated_input_flags_handler)
diff --git a/docs/code_snippets/error_handling/snippet4.py b/docs/code_snippets/error_handling/snippet4.py
index 972eff9..9d6efdf 100644
--- a/docs/code_snippets/error_handling/snippet4.py
+++ b/docs/code_snippets/error_handling/snippet4.py
@@ -1,9 +1,7 @@
from argenta import App
-
def empty_command_handler():
print("Empty input command")
-
app: App = App()
app.set_empty_command_handler(empty_command_handler)
diff --git a/docs/code_snippets/error_handling/snippet5.py b/docs/code_snippets/error_handling/snippet5.py
index 575ffab..2ea24fb 100644
--- a/docs/code_snippets/error_handling/snippet5.py
+++ b/docs/code_snippets/error_handling/snippet5.py
@@ -1,10 +1,8 @@
from argenta import App
from argenta.command import InputCommand
-
def unknown_command_handler(command: InputCommand):
print(f"Unknown input command with trigger: {command.trigger}")
-
app: App = App()
app.set_unknown_command_handler(unknown_command_handler)
diff --git a/docs/code_snippets/error_handling/snippet6.py b/docs/code_snippets/error_handling/snippet6.py
index 0dcda57..b14f60b 100644
--- a/docs/code_snippets/error_handling/snippet6.py
+++ b/docs/code_snippets/error_handling/snippet6.py
@@ -1,9 +1,7 @@
from argenta import App, Response
-
def exit_command_handler(response: Response):
print("Exit command handler")
-
app: App = App()
app.set_exit_command_handler(exit_command_handler)
diff --git a/docs/code_snippets/quickstart/simple_app.py b/docs/code_snippets/quickstart/simple_app.py
index b31b281..83f8808 100644
--- a/docs/code_snippets/quickstart/simple_app.py
+++ b/docs/code_snippets/quickstart/simple_app.py
@@ -12,12 +12,13 @@ orchestrator = Orchestrator()
# 2. Создание роутера для группировки команд
main_router = Router(title="Основные команды")
+
# 3. Определение команды и её обработчика
-@main_router.command(Command(
- "hello",
- description="Печатает приветственное сообщение",
- flags=Flag("name")
-))
+@main_router.command(
+ Command(
+ "hello", description="Печатает приветственное сообщение", flags=Flag("name")
+ )
+)
def hello_handler(response: Response):
"""Этот обработчик будет вызван для команды 'hello'."""
name = response.input_flags.get_flag_by_name("name")
@@ -26,6 +27,7 @@ def hello_handler(response: Response):
else:
print("Привет, мир!")
+
# 4. Подключение роутера к приложению
app.include_router(main_router)
diff --git a/docs/code_snippets/quickstart/task_manager/handlers.py b/docs/code_snippets/quickstart/task_manager/handlers.py
index 2345645..9897654 100644
--- a/docs/code_snippets/quickstart/task_manager/handlers.py
+++ b/docs/code_snippets/quickstart/task_manager/handlers.py
@@ -1,8 +1,7 @@
from typing import cast
from argenta import Command, Response, Router
-from argenta.command import Flag, Flags
-from argenta.command.flag.models import ValidationStatus
+from argenta.command.flag import ValidationStatus, Flag, Flags
from argenta.di import FromDishka
from .repository import Priority, Task, TaskRepository
@@ -24,14 +23,12 @@ router = Router(title="Task Manager")
)
def add_task(response: Response, repo: FromDishka[TaskRepository]):
description_flag = response.input_flags.get_flag_by_name("description")
-
- if not description_flag or not description_flag.input_value:
+ if not description_flag or not description_flag.status == ValidationStatus.VALID:
print("Error: --description flag is required.")
return
- task_description = description_flag.input_value
+ task_description = description_flag.input_value or ""
priority_flag = response.input_flags.get_flag_by_name("priority")
-
if priority_flag and priority_flag.status == ValidationStatus.VALID:
priority_value = priority_flag.input_value
else:
diff --git a/docs/code_snippets/quickstart/task_manager/main.py b/docs/code_snippets/quickstart/task_manager/main.py
index 09e758a..f44d139 100644
--- a/docs/code_snippets/quickstart/task_manager/main.py
+++ b/docs/code_snippets/quickstart/task_manager/main.py
@@ -3,16 +3,16 @@ from argenta import App, Orchestrator
from .handlers import router
from .provider import TaskProvider
-# 1. Создаем экземпляр приложения
+# 1. Создаем экземпляр приложения и оркестратора
app = App(
initial_message="Task Manager",
prompt="Enter a command: ",
)
+orchestrator = Orchestrator(custom_providers=[TaskProvider()])
# 2. Подключаем роутер с нашими командами
app.include_router(router)
-# 3. Создаем и запускаем оркестратор
+# 3. Запускаем поллинг через оркестратор
if __name__ == "__main__":
- orchestrator = Orchestrator(custom_providers=[TaskProvider()])
orchestrator.start_polling(app)
diff --git a/docs/code_snippets/response/data_sharing.py b/docs/code_snippets/response/data_sharing.py
index 72356f3..f9a92eb 100644
--- a/docs/code_snippets/response/data_sharing.py
+++ b/docs/code_snippets/response/data_sharing.py
@@ -3,19 +3,23 @@ from argenta.command import Flag
from argenta.di import FromDishka
# 1. Создаём роутер
-router = Router(title='Authentication')
+router = Router(title="Authentication")
+
# 2. Определяем сервис и обработчики
def authenticate_user(username: str) -> str:
"""Возвращает фиктивный токен для пользователя."""
return f"token_for_{username}"
-@router.command(Command('login', flags=Flag('username')))
+
+@router.command(Command("login", flags=Flag("username")))
def login_handler(response: Response, data_bridge: FromDishka[DataBridge]):
"""Обработчик для команды 'login'. Сохраняет токен в хранилище."""
- username_flag = response.input_flags.get_flag_by_name('username')
+ username_flag = response.input_flags.get_flag_by_name("username")
if not username_flag or not username_flag.input_value:
- print("[red]Ошибка:[/red] необходимо указать имя пользователя с помощью флага --username.")
+ print(
+ "[red]Ошибка:[/red] необходимо указать имя пользователя с помощью флага --username."
+ )
return
username = username_flag.input_value
@@ -25,19 +29,23 @@ def login_handler(response: Response, data_bridge: FromDishka[DataBridge]):
data_bridge.update({"auth_token": token})
print(f"[green]Успешный вход![/green] Пользователь '{username}' аутентифицирован.")
-@router.command('get-profile')
+
+@router.command("get-profile")
def get_profile_handler(response: Response, data_bridge: FromDishka[DataBridge]):
"""Обработчик для команды 'get-profile'. Использует токен из хранилища."""
session_data = data_bridge.get_all()
token = session_data.get("auth_token")
if not token:
- print("[red]Ошибка:[/red] вы не аутентифицированы. Сначала выполните команду 'login'.")
+ print(
+ "[red]Ошибка:[/red] вы не аутентифицированы. Сначала выполните команду 'login'."
+ )
return
print(f"Загрузка профиля с использованием токена: [yellow]{token}[/yellow]")
-@router.command('logout')
+
+@router.command("logout")
def logout_handler(response: Response, data_bridge: FromDishka[DataBridge]):
"""Обработчик для команды 'logout'. Очищает токен."""
try:
@@ -45,4 +53,3 @@ def logout_handler(response: Response, data_bridge: FromDishka[DataBridge]):
print("[green]Выход выполнен.[/green] Данные сессии очищены.")
except KeyError:
print("Вы и так не были аутентифицированы.")
-
diff --git a/docs/code_snippets/testing/app_integration_unittest.py b/docs/code_snippets/testing/app_integration_unittest.py
index c4638a9..4f1ef30 100644
--- a/docs/code_snippets/testing/app_integration_unittest.py
+++ b/docs/code_snippets/testing/app_integration_unittest.py
@@ -14,6 +14,7 @@ class TestAppIntegration(unittest.TestCase):
@self.router.command(Command("HELP", description="Show help"))
def help_cmd(response: Response):
print("Available commands: HELP")
+
_ = help_cmd # appease linter: function is registered via decorator
self.app.include_router(self.router)
@@ -22,5 +23,3 @@ class TestAppIntegration(unittest.TestCase):
with redirect_stdout(io.StringIO()) as stdout:
self.router.finds_appropriate_handler(InputCommand.parse("HELP"))
self.assertIn("Available commands:", stdout.getvalue())
-
-
diff --git a/docs/code_snippets/testing/di_handler_unittest.py b/docs/code_snippets/testing/di_handler_unittest.py
index 789ee96..63ff89f 100644
--- a/docs/code_snippets/testing/di_handler_unittest.py
+++ b/docs/code_snippets/testing/di_handler_unittest.py
@@ -15,6 +15,7 @@ class Service:
def hello(self) -> str:
return "world"
+
router = Router(title="DI")
@@ -56,5 +57,3 @@ class TestDIHandler(unittest.TestCase):
class _FakeApp:
# Minimal stub for setup_dishka; app object is not used in unit tests
registered_routers = []
-
-
diff --git a/docs/code_snippets/testing/simple_handler_unittest.py b/docs/code_snippets/testing/simple_handler_unittest.py
index 81a5e04..3d0c835 100644
--- a/docs/code_snippets/testing/simple_handler_unittest.py
+++ b/docs/code_snippets/testing/simple_handler_unittest.py
@@ -20,5 +20,3 @@ class TestSimpleHandler(unittest.TestCase):
with redirect_stdout(io.StringIO()) as stdout:
router.finds_appropriate_handler(InputCommand.parse("PING"))
self.assertIn("PONG", stdout.getvalue())
-
-
diff --git a/docs/index.rst b/docs/index.rst
index ef5a4c9..d600571 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -6,13 +6,16 @@
Argenta
=======
+Что это и зачем?
+----------------
+
**Библиотека для построения модульных CLI-приложений с простым и приятным API.**
Если у вас есть функциональность, которую вы хотите предоставить в виде CLI-приложения, Argenta поможет вам в этом.
Основная цель библиотеки — дать разработчикам возможность сосредоточиться на реализации своих идей, предоставляя для этого удобные абстракции.
.. image:: https://github.com/koloideal/Argenta/blob/main/imgs/mock_app_preview4.png?raw=True
- :alt: Пример приложения
+ :alt: App example
Argenta предназначена для создания приложений, работающих в собственном контексте (scope). Это означает, что при запуске пользователь входит в интерактивную сессию, где ему доступна вся реализованная вами функциональность.
@@ -22,9 +25,11 @@ Argenta предназначена для создания приложений,
* **Интерактивные сессии**. В отличие от традиционных CLI-инструментов, Argenta создаёт циклические сессии, позволяя пользователю выполнять команды последовательно, не перезапуская приложение.
* **Декларативный синтаксис**. Команды и их обработчики объявляются с помощью простых декораторов, что делает код чистым и интуитивно понятным.
-* **Встроенное внедрение зависимостей (DI)**. Благодаря интеграции с `dishka `_, вы можете легко внедрять сервисы (например, подключения к БД) прямо в обработчики команд, что упрощает их тестирование и переиспользование.
+* **Нативный DI**. Благодаря интеграции с `dishka `_, вы можете легко внедрять зависимости прямо в обработчики команд, что упрощает их тестирование и переиспользование.
* **Автоматическая валидация и парсинг**. Библиотека берёт на себя обработку флагов и аргументов командной строки, включая их парсинг, валидацию и преобразование типов.
-* **Гибкая настройка**. Вы можете легко кастомизировать системные сообщения, форматирование вывода и даже перенаправлять стандартный вывод (stdout) в свои обработчики.
+* **Гибкая настройка**. Вы можете легко кастомизировать системные сообщения, форматирование вывода и т.д.
+
+-----
Архитектура и жизненный цикл
-----------------------------
diff --git a/docs/root/quickstart.rst b/docs/root/quickstart.rst
index ee32893..30df561 100644
--- a/docs/root/quickstart.rst
+++ b/docs/root/quickstart.rst
@@ -5,12 +5,18 @@
В этом руководстве мы рассмотрим два примера создания CLI-приложения с помощью Argenta:
-* **Простой пример**: Быстрое знакомство с основными компонентами, такими как `App`, `Command` и `Router`.
+* **Простой пример**: Минимальное приложение, быстрое знакомство с основными компонентами.
* **Более сложный пример**: Полнофункциональное приложение «Менеджер задач» с внедрением зависимостей и бизнес-логикой.
Простой пример
---------------
+**Установка**
+
+.. code-block:: shell
+
+ pip install argenta
+
Этот пример демонстрирует абсолютный минимум, необходимый для создания и запуска приложения. Вы можете скопировать этот код, запустить его и сразу увидеть результат.
.. literalinclude:: ../code_snippets/quickstart/simple_app.py
@@ -21,6 +27,8 @@
.. image:: https://i.ibb.co/JwK9Vv4j/2025-11-03-135118.png
:alt: Simple App Example
+
+-----
Более сложный пример: Менеджер задач
--------------------------------------
@@ -43,7 +51,7 @@
3. **Создание провайдера для DI**
-Чтобы Argenta могла внедрять `TaskRepository` в наши обработчики, мы создадим провайдер для `dishka`.
+Чтобы Argenta могла внедрять ``TaskRepository`` в наши обработчики, мы создадим провайдер для ``dishka``.
.. literalinclude:: ../code_snippets/quickstart/task_manager/provider.py
:language: python
@@ -51,7 +59,7 @@
4. **Создание обработчиков команд**
-Теперь создадим обработчики для команд `add-task` и `list-tasks`. Обратите внимание, как мы используем флаги и внедряем `TaskRepository`.
+Теперь создадим обработчики для команд ``add-task`` и ``list-tasks``. Обратите внимание, как мы используем флаги и внедряем ``TaskRepository``.
.. literalinclude:: ../code_snippets/quickstart/task_manager/handlers.py
:language: python
@@ -59,7 +67,7 @@
5. **Сборка и запуск приложения**
-Наконец, соберем все вместе: создадим экземпляр `App`, подключим роутер и провайдер, а затем запустим приложение.
+Наконец, соберем все вместе: создадим экземпляр ``App``, подключим роутер и провайдер, а затем запустим приложение.
.. literalinclude:: ../code_snippets/quickstart/task_manager/main.py
:language: python
@@ -67,7 +75,7 @@
6. **Результат**
-Теперь вы можете запустить `main.py` и взаимодействовать с вашим новым CLI-приложением.
+Теперь вы можете запустить ``main.py`` и взаимодействовать с вашим новым CLI-приложением.
.. image:: https://i.ibb.co/bgsCLZhP/image.png
:alt: Task Manager Example
diff --git a/translator.py b/translator.py
new file mode 100644
index 0000000..e0d73ef
--- /dev/null
+++ b/translator.py
@@ -0,0 +1,71 @@
+import polib
+import deepl
+import getopt
+import sys
+import re
+
+DEEPL_API_TOKEN = 'ADD YOUR API KEY HERE!'
+
+global argv
+global opts
+global args
+
+argv = sys.argv[1:]
+opts, args = getopt.getopt(argv, "f:l:")
+
+def translate(text, lang):
+ # Define a dictionary to hold the mappings of tokens to placeholders
+ placeholders = {}
+
+ # Use a regular expression to find all the tokens
+ tokens = re.findall(r'%\((.*?)\)s', text)
+
+ # Replace each token with a unique placeholder
+ for i, token in enumerate(tokens):
+ placeholder = f'__PLACEHOLDER_{i}__'
+ placeholders[placeholder] = f'%({token})s'
+ text = text.replace(f'%({token})s', placeholder)
+
+ # Perform the translation
+ translator = deepl.Translator(DEEPL_API_TOKEN)
+ translated_text = str(translator.translate_text(text, target_lang=lang))
+
+ # Replace the placeholders back with the original tokens
+ for placeholder, token in placeholders.items():
+ translated_text = translated_text.replace(placeholder, token)
+
+ return translated_text
+
+def get_filename():
+ # read arguments from command line
+ for opt, arg in opts:
+ if opt in ['-f']:
+ filename = arg
+ if not filename:
+ print('Please enter the filename of the PO file e.g. /directory/django.po:')
+ filename = input()
+ return filename
+
+def get_target_language():
+ # read arguments from command line
+ for opt, arg in opts:
+ if opt in ['-l']:
+ lang = arg
+ if not lang:
+ print('Please enter two letter ISO language code e.g. DE:')
+ lang = input()
+ return lang
+
+def process_file(filename, lang):
+ po = polib.pofile(filename)
+ for entry in po.untranslated_entries():
+ if not entry.msgstr:
+ print(entry.msgid)
+ print('translating...')
+ entry.msgstr = translate(entry.msgid, lang)
+ print(entry.msgstr)
+ print('\n')
+ po.save(filename)
+
+if __name__ == '__main__':
+ process_file(get_filename(), get_target_language())
\ No newline at end of file