Update documentation and code snippets

This commit is contained in:
2025-12-04 20:23:11 +03:00
parent 723ed2210f
commit 087c76fed3
19 changed files with 278 additions and 114 deletions
@@ -0,0 +1,24 @@
from argenta import Router, Response
from argenta.command import Command, Flag, PossibleValues
from argenta.command.flag import ValidationStatus
router = Router()
@router.command(Command(
"deploy",
flags=Flag("verbose", possible_values=PossibleValues.NEITHER)
))
def deploy_handler(response: Response):
# Check for toggle flag presence
verbose_flag = response.input_flags.get_flag_by_name("verbose")
if verbose_flag and verbose_flag.status == ValidationStatus.VALID:
print("Deploying with verbose output...")
# Detailed logic
elif verbose_flag and verbose_flag.status == ValidationStatus.INVALID:
print("Incorrect flag value")
return
else:
print("Deploying...")
# Normal logic
+16
View File
@@ -0,0 +1,16 @@
from argenta import Router, Response
from argenta.command import Command, Flag
router = Router()
@router.command(Command("greet", flags=Flag("name")))
def greet_handler(response: Response):
# Get flag by name
name_flag = response.input_flags.get_flag_by_name("name")
# Check if flag was passed
if name_flag:
print(f"Hello, {name_flag.input_value}!")
else:
print("Hello, stranger!")
+2 -2
View File
@@ -5,9 +5,9 @@ flag_with_value = InputFlag(
) )
flag_without_value = InputFlag( flag_without_value = InputFlag(
name="help", prefix="-", input_value=None, status=ValidationStatus.VALID name="help", prefix="-", input_value='', status=ValidationStatus.VALID
) )
# String representation includes value # String representation includes value
print(str(flag_with_value)) # --output result.txt print(str(flag_with_value)) # --output result.txt
print(str(flag_without_value)) # -help None print(str(flag_without_value)) # -help
@@ -0,0 +1,68 @@
import operator
import re
from argenta import App, Orchestrator, Response, Router
from argenta.app import DynamicDividingLine
from argenta.command import Command, Flag, Flags
from argenta.response.status import ResponseStatus
router = Router("Calculator")
operations = {
'mul': operator.mul,
'sub': operator.sub,
'add': operator.add
}
@router.command(
Command(
"calc",
description="Calculator with two numbers",
flags=Flags(
[
Flag("a", possible_values=re.compile(r"^\d{,5}$")), # First number
Flag("b", possible_values=re.compile(r"^\d{,5}$")), # Second number
Flag("operation", possible_values=["add", "sub", "mul"]), # Operation: add, sub, mul
]
),
)
)
def calc_handler(response: Response):
# Get flag values
a_flag = response.input_flags.get_flag_by_name("a")
b_flag = response.input_flags.get_flag_by_name("b")
op_flag = response.input_flags.get_flag_by_name("op")
# Check that all flags are provided
if response.status != ResponseStatus.ALL_FLAGS_VALID or not all([a_flag, b_flag, op_flag]):
print("Error: must specify --a, --b and --op")
return
a = float(a_flag.input_value)
b = float(b_flag.input_value)
operation = op_flag.input_value
try:
result = operations[operation](a, b)
except ZeroDivisionError:
print("Can't divide by zero")
else:
print(f"Result: {result}")
app = App(
initial_message="Calculator",
repeat_command_groups_printing=False,
prompt=">> ",
dividing_line=DynamicDividingLine("~"),
)
orchestrator = Orchestrator()
def main():
app.include_router(router)
orchestrator.start_polling(app)
if __name__ == "__main__":
main()
+2 -2
View File
@@ -17,9 +17,9 @@ Argenta
.. image:: https://github.com/koloideal/Argenta/blob/main/imgs/mock_app_preview4.png?raw=True .. image:: https://github.com/koloideal/Argenta/blob/main/imgs/mock_app_preview4.png?raw=True
:alt: App example :alt: App example
Argenta предназначена для создания приложений, работающих в собственном контексте (scope). Это означает, что при запуске пользователь входит в интерактивную сессию, где ему доступна вся реализованная вами функциональность. Argenta предназначена для создания приложений, работающих в собственном контексте (scope). Это означает, что приложение запускается один раз и создаёт интерактивную сессию, похожую на Python REPL или MySQL консоль. При запуске пользователь входит в эту сессию, где ему доступна вся реализованная вами функциональность.
Один из ключевых принципов библиотеки — цикличность. После выполнения команды пользователь остаётся в интерактивной сессии, в отличие от таких библиотек, как ``argparse``, ``click`` и ``typer``. Выход из сессии контролируется пользователем. Один из ключевых принципов библиотеки — цикличность. После выполнения команды пользователь остаётся в интерактивной сессии и имеет доступ к созданной вами функциональности, в отличие от таких библиотек, как ``argparse``, ``click`` и ``typer``. Выход из сессии контролируется пользователем.
**Ключевые особенности:** **Ключевые особенности:**
@@ -64,7 +64,7 @@ msgid ""
"к БД или API-клиентов) в DI-контейнер." "к БД или API-клиентов) в DI-контейнер."
msgstr "" msgstr ""
"``custom_providers``: List of custom ``dishka.Provider`` providers for adding your services " "``custom_providers``: List of custom ``dishka.Provider`` providers for adding your services "
"(e.g., database connections or API clients) to the DI container." "(e.g., database connections or API clients) to the``di``container."
#: ../../root/api/orchestrator/index.rst:32 #: ../../root/api/orchestrator/index.rst:32
msgid "" msgid ""
@@ -93,7 +93,7 @@ msgid "Как это работает?"
msgstr "How Does It Work?" msgstr "How Does It Work?"
#: ../../root/dependency_injection.rst:48 #: ../../root/dependency_injection.rst:48
msgid "В основе DI в Argenta лежат **провайдеры** и **контейнер**." msgid "В основе``di``в Argenta лежат **провайдеры** и **контейнер**."
msgstr "DI in Argenta is based on **providers** and a **container**." msgstr "DI in Argenta is based on **providers** and a **container**."
#: ../../root/dependency_injection.rst:50 #: ../../root/dependency_injection.rst:50
+1 -1
View File
@@ -30,7 +30,7 @@ InputFlag
.. py:attribute:: input_value .. py:attribute:: input_value
Значение, переданное с флагом. Может быть `None` для флагов без значений. Значение, переданное с флагом. Может быть ``''`` (пустой строкой) для флагов без значений.
.. py:attribute:: status .. py:attribute:: status
:no-index: :no-index:
+1 -1
View File
@@ -44,7 +44,7 @@ ArgParser
Лучшие практики Лучшие практики
--------------- ---------------
Использовать атрибут ``parsed_argspace`` рекомендуется только на этапе настройки приложения. В обработчиках лучшей практикой является получение ``ArgSpace`` через DI. Подробнее см. :ref:`здесь <root_dependency_injection>`. Использовать атрибут ``parsed_argspace`` рекомендуется только на этапе настройки приложения. В обработчиках лучшей практикой является получение ``ArgSpace`` через ``di``. Подробнее см. :ref:`здесь <root_dependency_injection>`.
**Пример использования:** **Пример использования:**
+1 -1
View File
@@ -28,7 +28,7 @@ Orchestrator
Создаёт и конфигурирует экземпляр ``Orchestrator``. Создаёт и конфигурирует экземпляр ``Orchestrator``.
* ``arg_parser``: Экземпляр ``ArgParser``, отвечающий за парсинг аргументов командной строки при запуске скрипта (не путать с командами в интерактивном режиме). * ``arg_parser``: Экземпляр ``ArgParser``, отвечающий за парсинг аргументов командной строки при запуске скрипта (не путать с командами в интерактивном режиме).
* ``custom_providers``: Список пользовательских провайдеров ``dishka.Provider`` для добавления ваших сервисов (например, подключений к БД или API-клиентов) в DI-контейнер. * ``custom_providers``: Список пользовательских провайдеров ``dishka.Provider`` для добавления ваших сервисов (например, подключений к БД или API-клиентов) в di-контейнер.
* ``auto_inject_handlers``: Если **True** (по умолчанию), ``dishka`` автоматически внедрит зависимости в обработчики команд, инспектируя их сигнатуры. * ``auto_inject_handlers``: Если **True** (по умолчанию), ``dishka`` автоматически внедрит зависимости в обработчики команд, инспектируя их сигнатуры.
----- -----
+8 -5
View File
@@ -3,9 +3,9 @@
Внедрение зависимостей Внедрение зависимостей
======================= =======================
Внедрение зависимостей (Dependency Injection, DI) — это паттерн проектирования, который помогает писать слабосвязанный, легко тестируемый и расширяемый код. Вместо того чтобы обработчики сами создавали нужные им объекты (зависимости), они получают их извне. Внедрение зависимостей (Dependency Injection, ``di`` ) — это паттерн проектирования, который помогает писать слабосвязанный, легко тестируемый и расширяемый код. Вместо того чтобы обработчики сами создавали нужные им объекты (зависимости), они получают их извне.
``Argenta`` использует библиотеку ``dishka`` для реализации DI, что позволяет декларативно объявлять зависимости прямо в сигнатурах ваших обработчиков. ``Argenta`` использует библиотеку ``dishka`` для реализации ``di``, что позволяет декларативно объявлять зависимости прямо в сигнатурах ваших обработчиков.
Подробнее о **DI**, **IoC** и API для создания провайдеров можно прочитать в `официальной документации dishka <https://dishka.readthedocs.io/en/stable/di_intro.html>`_. Подробнее о **DI**, **IoC** и API для создания провайдеров можно прочитать в `официальной документации dishka <https://dishka.readthedocs.io/en/stable/di_intro.html>`_.
----- -----
@@ -32,7 +32,10 @@
:language: python :language: python
:linenos: :linenos:
После создания провайдера его необходимо зарегистрировать в оркестраторе: После создания провайдера его необходимо зарегистрировать в оркестраторе.
.. note::
Провайдеры регистрируются в ``Orchestrator``, а не в ``App``, так как оркестратор отвечает за настройку di-контейнера на уровне всего приложения. Вы можете передать список из нескольких провайдеров через параметр ``custom_providers``.
**Пример использования:** **Пример использования:**
@@ -45,7 +48,7 @@
Как это работает? Как это работает?
----------------- -----------------
В основе DI в Argenta лежат **провайдеры** и **контейнер**. В основе ``di`` в Argenta лежат **провайдеры** и **контейнер**.
* **Провайдер (Provider)** — это "рецепт", который объясняет, как создавать и настраивать ту или иную зависимость (например, подключение к БД, API-клиент или любой другой сервис). * **Провайдер (Provider)** — это "рецепт", который объясняет, как создавать и настраивать ту или иную зависимость (например, подключение к БД, API-клиент или любой другой сервис).
* **Контейнер (IoC Container)** — это "фабрика", которая хранит все рецепты (провайдеры) и по запросу создаёт и выдаёт готовые зависимости. * **Контейнер (IoC Container)** — это "фабрика", которая хранит все рецепты (провайдеры) и по запросу создаёт и выдаёт готовые зависимости.
@@ -68,7 +71,7 @@
Обмен данными между обработчиками Обмен данными между обработчиками
---------------------------------- ----------------------------------
Помимо DI, обработчики могут обмениваться данными в рамках сессии через **объект контекста**. В ``Argenta`` эту роль выполняет объект ``DataBridge``. Помимо ``di``, обработчики могут обмениваться данными в рамках сессии через **объект контекста**. В ``Argenta`` эту роль выполняет объект ``DataBridge``.
Каждый обработчик может записывать в него данные, а также читать, обновлять и удалять их. Каждый обработчик может записывать в него данные, а также читать, обновлять и удалять их.
+32 -2
View File
@@ -1,7 +1,7 @@
.. _root_flags: .. _root_flags:
Флаги вводимых команд Флаги команд
===================== ============
Флаги — это специальные параметры, которые пользователь может добавлять к командам для управления их поведением. Флаги — это специальные параметры, которые пользователь может добавлять к командам для управления их поведением.
@@ -56,6 +56,36 @@
Флаг состоит из префикса (``-``, ``--`` или ``---``), имени и, опционально, значения, которое указывается через пробел. Флаг состоит из префикса (``-``, ``--`` или ``---``), имени и, опционально, значения, которое указывается через пробел.
**Примеры:**
.. code-block:: shell
greet --name John # Flag with value
deploy --verbose # Flag without value (switch)
backup -f --compress # Several flags
-----
Работа с флагами в обработчиках
--------------------------------
Чтобы получить значение флага в обработчике, используйте объект ``response.input_flags`` типа :ref:`InputFlags <root_api_command_input_flags>`.
**Пример с флагом, имеющим значение:**
.. literalinclude:: ../code_snippets/flags/greet_handler.py
:language: python
:linenos:
**Пример с флагом-переключателем:**
.. literalinclude:: ../code_snippets/flags/deploy_handler.py
:language: python
:linenos:
.. seealso::
Подробнее о работе с объектом ``InputFlags`` см. в разделе :ref:`InputFlags <root_api_command_input_flags>`.
----- -----
Два типа флагов Два типа флагов
+30 -2
View File
@@ -23,6 +23,14 @@
:language: python :language: python
:linenos: :linenos:
**Запуск**
Сохраните код в файл (например, ``main.py``) и запустите:
.. code-block:: shell
python main.py
**Результат** **Результат**
.. image:: https://i.ibb.co/35q24Bh8/image.png .. image:: https://i.ibb.co/35q24Bh8/image.png
@@ -30,10 +38,30 @@
----- -----
Более сложный пример: Менеджер задач Промежуточный пример: Калькулятор с флагами
--------------------------------------------
Прежде чем перейти к сложному примеру с ``di``, рассмотрим промежуточный вариант — калькулятор, который использует флаги для управления поведением.
.. literalinclude:: ../code_snippets/quickstart/calculator_app.py
:language: python
:linenos:
**Использование:**
.. code-block:: shell
calc --a 10 --b 5 --operation add # Result: 15.0
calc --a 10 --b 5 --operation mul # Result: 50.0
Этот пример показывает, как работать с флагами без использования ``di``. Теперь перейдём к более сложному примеру.
-----
Сложный пример: Менеджер задач с ``di``
-------------------------------------- --------------------------------------
В этом руководстве мы создадим простое, но полнофункциональное CLI-приложение «Менеджер задач», которое продемонстрирует ключевые возможности Argenta. В этом руководстве мы создадим полнофункциональное CLI-приложение «Менеджер задач», которое продемонстрирует работу с внедрением зависимостей.
1. **Установка** 1. **Установка**
+14
View File
@@ -7,6 +7,20 @@
----- -----
Когда нужно отключать перехват stdout
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Отключайте перехват ``stdout`` (``disable_redirect_stdout=True`` в ``Router``), если ваши команды:
✓ Используют ``input()`` для интерактивного ввода данных от пользователя
✓ Используют прогресс-бары (``tqdm``, ``rich.progress``)
✓ Выводят данные в реальном времени (streaming, логи)
✓ Используют библиотеки, которые напрямую работают с ``stdout``
Для обычных команд с ``print()`` перехват можно оставить включённым — это не влияет на их работу.
-----
Механизм перехвата ``stdout`` Механизм перехвата ``stdout``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+13 -10
View File
@@ -5,6 +5,9 @@ from re import Pattern
from typing import Literal, override from typing import Literal, override
PREFIX_TYPE = Literal["-", "--", "---"]
class PossibleValues(Enum): class PossibleValues(Enum):
NEITHER = "NEITHER" NEITHER = "NEITHER"
ALL = "ALL" ALL = "ALL"
@@ -21,7 +24,7 @@ class Flag:
self, self,
name: str, name: str,
*, *,
prefix: Literal["-", "--", "---"] = "--", prefix: PREFIX_TYPE = "--",
possible_values: list[str] | Pattern[str] | PossibleValues = PossibleValues.ALL, possible_values: list[str] | Pattern[str] | PossibleValues = PossibleValues.ALL,
) -> None: ) -> None:
""" """
@@ -32,23 +35,23 @@ class Flag:
:return: None :return: None
""" """
self.name: str = name self.name: str = name
self.prefix: Literal["-", "--", "---"] = prefix self.prefix: PREFIX_TYPE = prefix
self.possible_values: list[str] | Pattern[str] | PossibleValues = possible_values self.possible_values: list[str] | Pattern[str] | PossibleValues = possible_values
def validate_input_flag_value(self, input_flag_value: str | None) -> bool: def validate_input_flag_value(self, input_flag_value: str) -> bool:
""" """
Private. Validates the input flag value Private. Validates the input flag value
:param input_flag_value: The input flag value to validate :param input_flag_value: The input flag value to validate
:return: whether the entered flag is valid as bool :return: whether the entered flag is valid as bool
""" """
if self.possible_values == PossibleValues.NEITHER: if self.possible_values == PossibleValues.NEITHER:
return input_flag_value is None return input_flag_value == ''
if self.possible_values == PossibleValues.ALL: if self.possible_values == PossibleValues.ALL:
return input_flag_value is not None return input_flag_value != ''
if isinstance(self.possible_values, Pattern): if isinstance(self.possible_values, Pattern):
return isinstance(input_flag_value, str) and bool(self.possible_values.match(input_flag_value)) return bool(self.possible_values.match(input_flag_value))
if isinstance(self.possible_values, list): if isinstance(self.possible_values, list):
return input_flag_value in self.possible_values return input_flag_value in self.possible_values
@@ -85,8 +88,8 @@ class InputFlag:
self, self,
name: str, name: str,
*, *,
prefix: Literal["-", "--", "---"] = "--", prefix: PREFIX_TYPE = "--",
input_value: str | None, input_value: str,
status: ValidationStatus | None, status: ValidationStatus | None,
): ):
""" """
@@ -97,8 +100,8 @@ class InputFlag:
:return: None :return: None
""" """
self.name: str = name self.name: str = name
self.prefix: Literal["-", "--", "---"] = prefix self.prefix: PREFIX_TYPE = prefix
self.input_value: str | None = input_value self.input_value: str = input_value
self.status: ValidationStatus | None = status self.status: ValidationStatus | None = status
@property @property
+48 -70
View File
@@ -1,6 +1,8 @@
__all__ = ["Command", "InputCommand"] __all__ = ["Command", "InputCommand"]
from typing import Literal, Never, Self, cast import shlex
from typing import Never, Self, cast
from typing_extensions import Literal
from argenta.command.exceptions import ( from argenta.command.exceptions import (
EmptyInputCommandException, EmptyInputCommandException,
@@ -14,6 +16,7 @@ ParseFlagsResult = tuple[InputFlags, str | None, str | None]
ParseResult = tuple[str, InputFlags] ParseResult = tuple[str, InputFlags]
MIN_FLAG_PREFIX: str = "-" MIN_FLAG_PREFIX: str = "-"
PREFIX_TYPE = Literal["-", "--", "---"]
DEFAULT_WITHOUT_FLAGS: Flags = Flags() DEFAULT_WITHOUT_FLAGS: Flags = Flags()
DEFAULT_WITHOUT_ALIASES: list[Never] = [] DEFAULT_WITHOUT_ALIASES: list[Never] = []
@@ -78,75 +81,50 @@ class InputCommand:
:param raw_command: raw input command :param raw_command: raw input command
:return: model of the input command, after parsing as InputCommand :return: model of the input command, after parsing as InputCommand
""" """
trigger, input_flags = CommandParser(raw_command).parse_raw_command() tokens = shlex.split(raw_command)
return cls(trigger=trigger, input_flags=input_flags) if not tokens:
raise EmptyInputCommandException
class CommandParser: command = tokens[0]
def __init__(self, raw_command: str) -> None: flags: InputFlags = InputFlags()
self.raw_command: str = raw_command
self._parsed_input_flags: InputFlags = InputFlags() i = 1
while i < len(tokens):
def parse_raw_command(self) -> ParseResult: token = tokens[i]
if not self.raw_command:
raise EmptyInputCommandException() if token.startswith("---"):
prefix = "---"
input_flags, crnt_flag_name, crnt_flag_val = self._parse_flags(self.raw_command.split()[1:]) name = token[3:]
elif token.startswith("--"):
if any([crnt_flag_name, crnt_flag_val]): prefix = "--"
raise UnprocessedInputFlagException() name = token[2:]
else: elif token.startswith("-"):
return (self.raw_command.split()[0], input_flags) prefix = "-"
name = token[1:]
def _parse_flags(self, _tokens: list[str] | list[Never]) -> ParseFlagsResult: else:
crnt_flg_name, crnt_flg_val = None, None raise UnprocessedInputFlagException
for index, token in enumerate(_tokens):
crnt_flg_name, crnt_flg_val = _parse_single_token(token, crnt_flg_name, crnt_flg_val) if not name:
raise UnprocessedInputFlagException
if not crnt_flg_name or self._is_next_token_value(index, _tokens):
continue if i + 1 < len(tokens) and not tokens[i + 1].startswith("-"):
input_value = tokens[i + 1]
i += 2
else:
input_value = ""
i += 1
input_flag = InputFlag( input_flag = InputFlag(
name=crnt_flg_name[crnt_flg_name.rfind(MIN_FLAG_PREFIX) + 1 :], name=name,
prefix=cast( prefix=cast(PREFIX_TYPE, prefix), # pyright: ignore[reportUnnecessaryCast]
Literal["-", "--", "---"], input_value=input_value,
crnt_flg_name[: crnt_flg_name.rfind(MIN_FLAG_PREFIX) + 1], status=None
),
input_value=crnt_flg_val,
status=None,
) )
if input_flag in self._parsed_input_flags: if input_flag in flags:
raise RepeatedInputFlagsException(input_flag) raise RepeatedInputFlagsException(input_flag)
self._parsed_input_flags.add_flag(input_flag) flags.add_flag(input_flag)
crnt_flg_name, crnt_flg_val = None, None
return cls(command, input_flags=flags)
return (self._parsed_input_flags, crnt_flg_name, crnt_flg_val)
def _is_next_token_value(self, current_index: int, _tokens: list[str] | list[Never]) -> bool:
next_index = current_index + 1
if next_index >= len(_tokens):
return False
next_token = _tokens[next_index]
return not next_token.startswith(MIN_FLAG_PREFIX)
def _parse_single_token(
token: str, crnt_flag_name: str | None, crnt_flag_val: str | None
) -> tuple[str | None, str | None]:
if not token.startswith(MIN_FLAG_PREFIX):
if not crnt_flag_name or crnt_flag_val:
raise UnprocessedInputFlagException
return crnt_flag_name, token
prefix = token[: token.rfind(MIN_FLAG_PREFIX)]
if len(token) < 2 or len(prefix) > 2:
raise UnprocessedInputFlagException
new_flag_name = token
new_flag_value = None
return new_flag_name, new_flag_value
+1 -1
View File
@@ -22,8 +22,8 @@ HandlerFunc: TypeAlias = Callable[..., None]
class Router: class Router:
def __init__( def __init__(
self, self,
*,
title: str | None = "Default title", title: str | None = "Default title",
*,
disable_redirect_stdout: bool = False, disable_redirect_stdout: bool = False,
): ):
""" """
+6 -6
View File
@@ -6,8 +6,8 @@ from argenta.command.exceptions import (EmptyInputCommandException,
UnprocessedInputFlagException) UnprocessedInputFlagException)
from argenta.command.flag import Flag, InputFlag from argenta.command.flag import Flag, InputFlag
from argenta.command.flag.flags import Flags from argenta.command.flag.flags import Flags
from argenta.command.flag.models import PossibleValues from argenta.command.flag.models import PossibleValues, ValidationStatus
from argenta.command.models import Command, InputCommand, ValidationStatus from argenta.command.models import Command, InputCommand
class TestInputCommand(unittest.TestCase): class TestInputCommand(unittest.TestCase):
@@ -28,7 +28,7 @@ class TestInputCommand(unittest.TestCase):
def test_validate_invalid_input_flag1(self): def test_validate_invalid_input_flag1(self):
command = Command('some', flags=Flag('test')) command = Command('some', flags=Flag('test'))
self.assertEqual(command.validate_input_flag(InputFlag('test', input_value=None, status=None)), ValidationStatus.INVALID) self.assertEqual(command.validate_input_flag(InputFlag('test', input_value='', status=None)), ValidationStatus.INVALID)
def test_validate_valid_input_flag2(self): def test_validate_valid_input_flag2(self):
command = Command('some', flags=Flags([Flag('test'), Flag('more')])) command = Command('some', flags=Flags([Flag('test'), Flag('more')]))
@@ -36,15 +36,15 @@ class TestInputCommand(unittest.TestCase):
def test_validate_undefined_input_flag1(self): def test_validate_undefined_input_flag1(self):
command = Command('some', flags=Flag('test')) command = Command('some', flags=Flag('test'))
self.assertEqual(command.validate_input_flag(InputFlag('more', input_value=None, status=None)), ValidationStatus.UNDEFINED) self.assertEqual(command.validate_input_flag(InputFlag('more', input_value='', status=None)), ValidationStatus.UNDEFINED)
def test_validate_undefined_input_flag2(self): def test_validate_undefined_input_flag2(self):
command = Command('some', flags=Flags([Flag('test'), Flag('more')])) command = Command('some', flags=Flags([Flag('test'), Flag('more')]))
self.assertEqual(command.validate_input_flag(InputFlag('case', input_value=None, status=None)), ValidationStatus.UNDEFINED) self.assertEqual(command.validate_input_flag(InputFlag('case', input_value='', status=None)), ValidationStatus.UNDEFINED)
def test_validate_undefined_input_flag3(self): def test_validate_undefined_input_flag3(self):
command = Command('some') command = Command('some')
self.assertEqual(command.validate_input_flag(InputFlag('case', input_value=None, status=None)), ValidationStatus.UNDEFINED) self.assertEqual(command.validate_input_flag(InputFlag('case', input_value='', status=None)), ValidationStatus.UNDEFINED)
def test_invalid_input_flag1(self): def test_invalid_input_flag1(self):
command = Command('some', flags=Flag('test', possible_values=PossibleValues.NEITHER)) command = Command('some', flags=Flag('test', possible_values=PossibleValues.NEITHER))
+9 -9
View File
@@ -29,8 +29,8 @@ class TestFlag(unittest.TestCase):
'--') '--')
def test_get_flag_value_without_set(self): def test_get_flag_value_without_set(self):
self.assertEqual(InputFlag(name='test', input_value=None, status=None).input_value, self.assertEqual(InputFlag(name='test', input_value='', status=None).input_value,
None) '')
def test_get_flag_value_with_set(self): def test_get_flag_value_with_set(self):
flag = InputFlag(name='test', input_value='example', status=None) flag = InputFlag(name='test', input_value='example', status=None)
@@ -54,11 +54,11 @@ class TestFlag(unittest.TestCase):
def test_validate_correct_empty_flag_value_without_possible_flag_values(self): def test_validate_correct_empty_flag_value_without_possible_flag_values(self):
flag = Flag(name='test', possible_values=PossibleValues.NEITHER) flag = Flag(name='test', possible_values=PossibleValues.NEITHER)
self.assertEqual(flag.validate_input_flag_value(None), True) self.assertEqual(flag.validate_input_flag_value(''), True)
def test_validate_correct_empty_flag_value_with_possible_flag_values(self): def test_validate_correct_empty_flag_value_with_possible_flag_values(self):
flag = Flag(name='test', possible_values=PossibleValues.NEITHER) flag = Flag(name='test', possible_values=PossibleValues.NEITHER)
self.assertEqual(flag.validate_input_flag_value(None), True) self.assertEqual(flag.validate_input_flag_value(''), True)
def test_validate_incorrect_random_flag_value_without_possible_flag_values(self): def test_validate_incorrect_random_flag_value_without_possible_flag_values(self):
flag = Flag(name='test', possible_values=PossibleValues.NEITHER) flag = Flag(name='test', possible_values=PossibleValues.NEITHER)
@@ -69,19 +69,19 @@ class TestFlag(unittest.TestCase):
self.assertEqual(flag.validate_input_flag_value('random value'), True) self.assertEqual(flag.validate_input_flag_value('random value'), True)
def test_get_input_flag1(self): def test_get_input_flag1(self):
flag = InputFlag(name='test', input_value=None, status=None) flag = InputFlag(name='test', input_value='', status=None)
input_flags = InputFlags([flag]) input_flags = InputFlags([flag])
self.assertEqual(input_flags.get_flag_by_name('test'), flag) self.assertEqual(input_flags.get_flag_by_name('test'), flag)
def test_get_input_flag2(self): def test_get_input_flag2(self):
flag = InputFlag(name='test', input_value=None, status=None) flag = InputFlag(name='test', input_value='', status=None)
flag2 = InputFlag(name='some', input_value=None, status=None) flag2 = InputFlag(name='some', input_value='', status=None)
input_flags = InputFlags([flag, flag2]) input_flags = InputFlags([flag, flag2])
self.assertEqual(input_flags.get_flag_by_name('some'), flag2) self.assertEqual(input_flags.get_flag_by_name('some'), flag2)
def test_get_undefined_input_flag(self): def test_get_undefined_input_flag(self):
flag = InputFlag(name='test', input_value=None, status=None) flag = InputFlag(name='test', input_value='', status=None)
flag2 = InputFlag(name='some', input_value=None, status=None) flag2 = InputFlag(name='some', input_value='', status=None)
input_flags = InputFlags([flag, flag2]) input_flags = InputFlags([flag, flag2])
self.assertEqual(input_flags.get_flag_by_name('case'), None) self.assertEqual(input_flags.get_flag_by_name('case'), None)