90 Commits

Author SHA1 Message Date
kolo 078cbdc4a8 fix typechecker error, chage version in readme and etc 2025-10-13 01:51:38 +03:00
kolo 462a8088e9 impl di in handlers with support custom provider 2025-10-13 01:13:28 +03:00
kolo e2753ef904 initial commit in new pr 2025-10-12 00:14:42 +03:00
kolo 8dfb95ec91 fix: docs config 2025-10-11 20:15:19 +03:00
kolo e67d08970f fix: docs config 2025-10-11 20:03:36 +03:00
kolo 1e5b220a22 feat/dcos 2025-10-11 19:59:50 +03:00
kolo 6f4f8c407a Merge pull request #2 from koloideal/feat/full_support_argparser
Added support for optional parameters for various types of arguments
2025-10-11 19:39:36 +03:00
kolo 2b76bea318 fix: tests, imports 2025-10-11 19:37:23 +03:00
kolo e481ee8775 extend arguments 2025-10-11 18:29:11 +03:00
kolo 0a1d462090 step by step 2025-10-09 22:01:06 +03:00
kolo b3b5e2e8a8 extend arguments 2025-10-09 20:23:52 +03:00
kolo 0bdc3f07c2 initial commit in new pr 2025-10-08 21:51:10 +03:00
kolo 77416cf22c release 1.1.1 2025-10-08 20:49:29 +03:00
kolo b6c84f1a91 fix ci 2025-10-08 13:44:12 +03:00
kolo c2d235e576 fix ci 2025-10-08 13:42:01 +03:00
kolo f7f5db58aa fix ci 2025-10-08 13:40:20 +03:00
kolo 73303b1c08 ref: typehints, enum instead of raw string, abc and other (#1)
Full code coverage with annotations, fixing errors in various linters: ruff, wps, etc. Fixing errors in type checkers: ty, mypy, pyright. Formatting and bringing code to a consistent style, applying best practices in various aspects.
2025-10-08 13:37:31 +03:00
kolo 22f1171192 fix: link to img in readme 2025-08-12 12:26:37 +03:00
kolo a844095fdc Update README.de.md 2025-08-06 14:57:31 +03:00
kolo a7c6a14705 Update README.md 2025-08-06 14:56:24 +03:00
kolo cfdb37330e Update README.ru.md 2025-08-06 14:52:33 +03:00
kolo aef6a9ca38 update .gitignore 2025-07-04 18:31:52 +03:00
kolo c8e0729be8 fix bugs 2025-05-27 14:19:54 +03:00
kolo c2bbc5f15d first steps sor adding metrics tests 2025-05-24 00:39:39 +03:00
kolo 0acbf54e44 first steps sor adding metrics tests 2025-05-23 22:12:12 +03:00
kolo c3d9541330 working 2025-05-23 14:33:13 +03:00
kolo f6561de9b3 wotk 2025-05-22 20:26:48 +03:00
kolo bebd84969b add Enum PossibleValues for bool values as values of possible_values argument in Flag 2025-05-22 12:10:32 +03:00
kolo 365347ea7f some fix 2025-05-21 17:01:35 +03:00
kolo 33cb528b1d some fix 2025-05-21 13:32:45 +03:00
kolo fd287c5da0 fix type hints with ty help, add ability to configure stdout capture when handling input by the router 2025-05-20 22:44:47 +03:00
kolo 45f410e3e8 make pre_cycle_setup faster on 4 sec, start implemtnation disable redirect stdout in router 2025-05-19 10:31:05 +03:00
kolo 8b06e9cd39 add metrics concept 2025-05-12 16:22:29 +03:00
kolo c38fe10006 translate readme on de 2025-05-10 22:07:52 +03:00
kolo 03cbc64f48 translate readme 2025-05-10 21:56:34 +03:00
kolo cbf7d3c578 new warning when triggers or aliases overlap 2025-05-10 21:31:59 +03:00
kolo ea2d068022 change dataclass to enum 2025-05-10 20:13:42 +03:00
kolo 5991851207 Update README.md 2025-05-10 00:29:03 +03:00
kolo f628c3b5b5 v1.0.2 2025-05-10 00:11:57 +03:00
kolo 05379712f4 some fix 2025-05-09 23:33:54 +03:00
kolo ed1cbf0fcf stable version 2025-05-09 23:27:34 +03:00
kolo 471f05369b Merge branch 'dev' of https://github.com/koloideal/Argenta into dev 2025-05-09 23:25:47 +03:00
kolo 13f7e33db1 ruff format 2025-05-09 23:25:21 +03:00
kolo 9a78aa9263 ruff check 2025-05-09 23:24:12 +03:00
kolo 58ccd6b26d Update README.md 2025-05-09 23:20:45 +03:00
kolo 73144f7ba4 Update README.md 2025-05-09 23:19:07 +03:00
kolo 650f4c9036 some fix 2025-05-09 18:23:08 +03:00
kolo 393f5c7d81 fix 2025-05-08 23:43:08 +03:00
kolo 9eb2bb6c46 new imgs 2025-05-08 23:37:42 +03:00
kolo 79b275eac7 some fix 2025-05-08 01:08:11 +03:00
kolo 07ac2af71e some fix 2025-05-07 19:14:19 +03:00
kolo c4b3aa7db8 working 2025-05-07 02:15:42 +03:00
kolo 61ef6a6466 all tests passed 2025-05-06 21:53:53 +03:00
kolo 477f3a7dec starting refactor tests 2025-05-04 16:40:10 +03:00
kolo adf3431388 more beautiful typehints warning 2025-05-04 03:08:54 +03:00
kolo 83955aa046 first beta - adding hints for similar commands, now - feature freezing 2025-05-04 02:13:05 +03:00
kolo 5a17e916eb work on stable major version 2025-04-30 15:48:38 +03:00
kolo 1159dda16e work on Response model 2025-04-30 00:08:49 +03:00
kolo 315508a36e work on Response 2025-04-29 20:40:47 +03:00
kolo 9d6598c4e0 work on Response model 2025-04-29 00:07:32 +03:00
kolo eb43806da6 new model - Response 2025-04-28 02:21:34 +03:00
kolo e076dbf84f new img in docs 2025-04-27 23:43:14 +03:00
kolo 2f090b6b47 new img in docs 2025-04-27 23:42:56 +03:00
kolo c9dbf2bbae fix print framed text with static dividing line 2025-04-27 23:27:08 +03:00
kolo e768c1bd2c fix 2025-04-27 21:29:14 +03:00
kolo 408450ec12 fix 2025-04-27 21:20:44 +03:00
kolo 106ca058be new tests 2025-04-27 14:11:01 +03:00
kolo b5ddfb3b35 new tests 2025-04-27 13:28:11 +03:00
kolo 61e4502e41 work, fix etc. 2025-04-26 22:23:35 +03:00
kolo 9b2fc87e33 release v1.0.0a1 2025-04-25 02:29:44 +03:00
kolo 89f09c42f8 pre-release v1.0.0 2025-04-24 21:26:41 +03:00
kolo 5bcae8fe68 Update README.md 2025-04-24 17:40:53 +03:00
kolo ca58008431 fix 2025-04-23 22:07:57 +03:00
kolo 30974f48eb fix 2025-04-23 22:06:10 +03:00
kolo df4ba080b0 new docs 2025-04-23 22:02:02 +03:00
kolo f93930d712 work 2025-04-23 21:45:04 +03:00
kolo 036c17ec9a last steps work on new docs, full complete write docstring for all objects 2025-04-23 20:54:03 +03:00
kolo 7281fdeabf new app preview img in docs 2025-04-23 20:50:31 +03:00
kolo 051ec6df28 steps 2025-04-20 23:58:15 +03:00
kolo 00a1e11fc1 new docs 2025-04-19 12:13:29 +03:00
kolo 584df9ba69 new docs 2025-04-18 12:50:30 +03:00
kolo a649022f1d work on docstrings 2025-04-15 23:49:51 +03:00
kolo 26a9d8a6da work on 2025-04-15 22:26:20 +03:00
kolo 9522b0161a work on 2025-04-15 01:09:03 +03:00
kolo e189f8d9aa big step 2025-04-14 16:38:53 +03:00
kolo 3ef8707cfa big step 2025-04-14 14:54:17 +03:00
kolo a5fdcab862 work on 2025-04-14 01:03:24 +03:00
kolo ba035881ee work on support args 2025-04-13 19:24:03 +03:00
kolo 34ebe55531 first steps 2025-04-13 14:39:53 +03:00
kolo 01c9d2dc6d first step 2025-04-13 14:12:08 +03:00
168 changed files with 8497 additions and 1700 deletions
+31
View File
@@ -0,0 +1,31 @@
name: mypy
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-python@v3
with:
python-version: "3.13"
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install mypy
pip install .
- name: Run type checker
run: mypy -p argenta
+3 -3
View File
@@ -2,9 +2,9 @@ name: ruff
on: on:
push: push:
branches: [ "kolo" ] branches: [ "main" ]
pull_request: pull_request:
branches: [ "kolo" ] branches: [ "main" ]
permissions: permissions:
contents: read contents: read
@@ -27,4 +27,4 @@ jobs:
pip install ruff pip install ruff
- name: Run linter - name: Run linter
run: ruff check ./argenta run: ruff check ./src
+5 -5
View File
@@ -2,9 +2,9 @@ name: tests
on: on:
push: push:
branches: [ "kolo" ] branches: [ "main" ]
pull_request: pull_request:
branches: [ "kolo" ] branches: [ "main" ]
permissions: permissions:
contents: read contents: read
@@ -24,8 +24,8 @@ jobs:
- name: Install dependencies - name: Install dependencies
run: | run: |
python -m pip install --upgrade pip python -m pip install --upgrade pip
pip install poetry pip install uv
poetry install uv sync --group dev
- name: Run tests - name: Run tests
run: poetry run python -m unittest discover run: uv run python -m unittest discover
+6 -3
View File
@@ -1,7 +1,10 @@
.venv *venv
.idea .idea
.vscode
dist dist
poetry.lock uv.lock
*__pycache__/ *__pycache__/
*.hist* *.hist*
build
source
*cache
+13
View File
@@ -0,0 +1,13 @@
version: 2
build:
os: ubuntu-22.04
tools:
python: "3.12"
python:
install:
- requirements: docs/requirements.txt
sphinx:
configuration: docs/conf.py
+69
View File
@@ -0,0 +1,69 @@
# Argenta
### Bibliothek zum Erstellen modularer CLI-Anwendungen
Mit Argenta können Sie die CLI-Funktionalität in isolierte, abstrahierte Umgebungen einkapseln. Zum Beispiel: Sie erstellen ein Dienstprogramm ähnlich dem Metasploit Framework, bei dem der Benutzer zuerst in einen bestimmten Scoop eintritt (z. B. ein Modul zum Scannen auswählt) und dann auf eine Reihe von Befehlen zugreift, die nur für diesen Kontext spezifisch sind. Argenta bietet eine einfache und prägnante Möglichkeit, eine solche Architektur zu konstruieren.
---
![preview](https://github.com/koloideal/Argenta/blob/main/imgs/mock_app_preview4.png?raw=True)
---
# Installation
```bash
pip install argenta
```
or
```bash
poetry add argenta
```
---
# Schnellstart
Ein Beispiel für eine einfache Anwendung
```python
# routers.py
from argenta.router import Router
from argenta.command import Command
from argenta.response import Response
router = Router()
@router.command(Command("hello"))
def handler(response: Response):
print("Hello, world!")
```
```python
# main.py
from argenta.app import App
from argenta.orchestrator import Orchestrator
from routers import router
app: App = App()
orchestrator: Orchestrator = Orchestrator()
def main() -> None:
app.include_router(router)
orchestrator.start_polling(app)
if __name__ == '__main__':
main()
```
---
# Funktionen in der Entwicklung
- Vollständige Unterstützung für Autocompleter unter Linux
## Vollständige [Dokumentation](https://argenta-docs.vercel.app) | MIT 2025 kolo | made by [kolo](https://t.me/kolo_id)
+24 -534
View File
@@ -1,16 +1,20 @@
# Argenta # Argenta
--- ### Library for creating modular CLI applications
## Описание #### RU - [README.ru.md](https://github.com/koloideal/Argenta/blob/main/README.ru.md) • DE - [README.de.md](https://github.com/koloideal/Argenta/blob/main/README.de.md)
**Argenta** — Python library for creating TUI
![preview](https://github.com/koloideal/Argenta/blob/kolo/imgs/mock_app_preview_last.png?raw=True)
Пример внешнего вида TUI, написанного с помощью Argenta
--- ---
# Установка Argenta allows you to encapsulate CLI functionality in isolated, abstracted environments. Eg: you are creating a utility similar to the Metasploit Framework, where the user first logs into a specific scope (for example, selects a module to scan), and then gets access to a set of commands specific only to that context. Argenta provides a simple and concise way to build such an architecture.
---
![preview](https://github.com/koloideal/Argenta/blob/main/imgs/mock_app_preview4.png?raw=True)
---
# Installing
```bash ```bash
pip install argenta pip install argenta
``` ```
@@ -21,563 +25,49 @@ poetry add argenta
--- ---
# Быстрый старт # Quick start
Пример простейшей оболочки с командой без зарегистрированных флагов An example of a simple application
```python ```python
# routers.py # routers.py
from argenta.router import Router from argenta.router import Router
from argenta.command import Command from argenta.command import Command
from argenta.response import Response
router = Router() router = Router()
@router.command(Command("hello")) @router.command(Command("hello"))
def handler(): def handler(response: Response):
print("Hello, world!") print("Hello, world!")
``` ```
```python ```python
# main.py # main.py
from argenta.app import App from argenta.app import App
from argenta.orchestrator import Orchestrator
from routers import router from routers import router
app: App = App() app: App = App()
orchestrator: Orchestrator = Orchestrator()
def main() -> None: def main() -> None:
app.include_router(router) app.include_router(router)
app.start_polling() orchestrator.start_polling(app)
if __name__ == '__main__': if __name__ == '__main__':
main() main()
``` ```
Пример оболочки с командой, у которой зарегистрированы флаги
```python
# routers.py
import re
from argenta.router import Router
from argenta.command import Command
from argenta.command.flag import Flags, Flag, InputFlags
router = Router()
registered_flags = Flags(
Flag(name='host',
prefix='--',
possible_values=re.compile(r'^192.168.\d{1,3}.\d{1,3}$')),
Flag('port', '--', re.compile(r'^[0-9]{1,4}$')))
@router.command(Command("hello"))
def handler():
print("Hello, world!")
@router.command(Command(trigger="ssh",
description='connect via ssh',
flags=registered_flags))
def handler_with_flags(flags: InputFlags):
for flag in flags:
print(f'Flag name: {flag.get_name()}\n'
f'Flag value: {flag.get_value()}')
```
--- ---
# *classes* : # Features in development
--- - Full support for autocompleter on Linux
## *class* : : `App` ## Full [docs](https://argenta-docs.vercel.app) | MIT 2025 kolo | made by [kolo](https://t.me/kolo_id)
Класс, определяющий поведение и состояние оболочки
### Конструктор
```python
App(prompt: str = '[italic dim bold]What do you want to do?\n',
initial_message: str = '\nArgenta\n',
farewell_message: str = '\nSee you\n',
exit_command: Command = Command('Q', 'Exit command'),
system_points_title: str | None = 'System points:',
ignore_command_register: bool = True,
dividing_line: StaticDividingLine | DynamicDividingLine = StaticDividingLine(),
repeat_command_groups: bool = True,
override_system_messages: bool = False,
autocompleter: AutoCompleter = AutoCompleter(),
print_func: Callable[[str], None] = Console().print)
```
**Аргументы:**
- **name : mean**
- `prompt` (`str`): Сообщение перед вводом команды.
- `initial_message` (`str`): Приветственное сообщение при запуске.
- `farewell_message` (`str`): Сообщение при выходе.
- `exit_command` (`Command`): Сущность команды, которая будет отвечать за завершение работы.
- `system_points_title` (`str`): Заголовок перед списком системных команд.
- `ignore_command_register` (`bool`): Игнорировать регистр всех команд.
- `dividing_line` (`StaticDividingLine | DynamicDividingLine`): Разделительная строка.
- `repeat_command_groups` (`bool`): Повторять описание команд перед вводом.
- `override_system_messages` (`bool`): Переопределить ли дефолтное оформление сообщений ([подробнее см.](#override_defaults))
- `autocompleter` (`AutoCompleter`): Сущность автодополнителя ввода.
- `print_func` (`Callable[[str], None]`): Функция вывода текста в терминал.
---
### ***methods***
---
#### **.start_polling() -> `None`**
*method mean* **::** Запускает цикл обработки ввода
---
#### **.include_router(router: Router) -> `None`**
*param* `router: Router` **::** Регистрируемый роутер
*required* **::** True
*method mean* **::** Регистрирует роутер в оболочке
---
#### **.include_routers(\*routers: Router) -> `None`**
*param* `routers: Router` **::** Неограниченное количество регистрируемых роутеров
*required* **::** True
*method mean* **::** Регистрирует роутер в оболочке
---
#### **.set_description_message_pattern(pattern: str) -> `None`**
*param* `pattern: str` **::** Паттерн описания команды при её выводе в консоль
*required* **::** True
*example* **::** `"[{command}] *=*=* {description}"`
*method mean* **::** Устанавливает паттерн описания команд, который будет использован
при выводе в консоль
---
#### **.add_message_on_startup(message: str) -> `None`**
*param* `message: str` **::** Сообщение, которое будет выведено при запуске приложения
*required* **::** True
*example* **::** `Message on startup`
*method mean* **::** Устанавливает паттерн описания команд, который будет использован
при выводе в консоль
---
<a name="custom_handler"></a>
#### **.repeated_input_flags_handler: `Callable[[str], None])`**
*example* **::** `lambda raw_command: print_func(f'Repeated input flags: "{raw_command}"')`
*attr mean* **::** Устанавливает функцию, которой будет передано управление при
вводе юзером повторяющихся флагов
---
#### **.invalid_input_flags_handler: `Callable[[str], None])`**
*example* **::** `lambda raw_command: print_func(f'Incorrect flag syntax: "{raw_command}"')`
*attr mean* **::** Устанавливает функцию, которой будет передано управление при
вводе юзером команды с некорректным синтаксисом флагов
---
#### **.unknown_command_handler: `Callable[[str], None]`**
*example* **::** `lambda command: print_func(f"Unknown command: {command.get_string_entity()}")`
*attr mean* **::** Устанавливает функцию, которой будет передано управление при
вводе юзером неизвестной команды
---
#### **.empty_command_handler: `Callable[[str], None])`**
*example* **::** `lambda: print_func(f'Empty input command')`
*attr mean* **::** Устанавливает функцию, которой будет передано управление при
вводе юзером пустой команды
---
### Примечания
- В устанавливаемом паттерне сообщения описания команды необходимы быть два ключевых слова:
`command` и `description`, каждое из которых должно быть заключено в фигурные скобки, после обработки
паттерна на места этих ключевых слов будут подставлены соответствующие атрибуты команды, при отсутствии
этих двух ключевых слов будет вызвано исключение `InvalidDescriptionMessagePatternException`
- Команды оболочки не должны повторяться, при значении атрибута `ignore_command_register` равным `True`
допускается создание обработчиков для разных регистров одинаковых символов в команде, для примера `u` и `U`,
при значении атрибута `ignore_command_register` класса `App` равным `False` тот же пример вызывает исключение
`RepeatedCommandInDifferentRoutersException`. Исключение вызывается только при наличии пересекающихся команд
у __<u>разных</u>__ роутеров
- Наиболее частые сообщение при запуске предопределены и доступны для быстрого
использования: `argenta.app.defaults.PredeterminedMessages`
<a name="override_defaults"></a>
- Если `override_system_messages`=`False`, то при переопределении таких атрибутов как `initial_message` и
`farawell_message` будет использовано дефолтное оформление текста, в виде красного ascii арта, при значении
`override_system_messages`=`True` системные сообщения будут отображены в точности какими были переданы
### Исключения
- `InvalidRouterInstanceException` — Переданный объект в метод `App().include_router()` не является экземпляром класса `Router`.
- `InvalidDescriptionMessagePatternException` — Неправильный формат паттерна описания команд.
- `IncorrectNumberOfHandlerArgsException` — У обработчика нестандартного поведения зарегистрировано неверное количество аргументов(в большинстве случаев у него должен быть один аргумент).
- `NoRegisteredHandlersException` — У роутера нет ни одного обработчика команд.
---
## *class* :: `AutoCompleter`
Класс, экземпляр которого представляет собой автодополнитель ввода
### Конструктор
```python
AutoCompleter(history_filename: str = False,
autocomplete_button: str = 'tab')
```
**Аргументы:**
- **name : mean**
- `history_filename` (`str` | `False`): Путь к файлу, который будет являться или является
историй пользовательского ввода, в последующем эти команды будут автодополняться, файл
может не существовать при инициализации, тогда он будет создан, при значении аргумента `False`
история пользовательского ввода будет существовать только в пределах сессии и не сохраняться в файл
- `autocomplete_button` (`str`): Строковое обозначение кнопки на клавиатуре, которая будет
использоваться для автодополнения при вводе, по умолчанию `tab`
---
## *class* :: `StaticDivideLine`
Класс, экземпляр которого представляет собой строковый разделитель фиксированной длины
### Конструктор
```python
StaticDivideLine(unit_part: str = '-',
length: int = 25)
```
**Аргументы:**
- **name : mean**
- `unit_part` (`str`): Единичная часть строкового разделителя
- `length` (`int`): Длина строкового разделителя
---
## *class* :: `DinamicDivideLine`
Строковый разделитель динамической длины, которая определяется длиной обрамляемого вывода команды
### Конструктор
```python
DinamicDivideLine(unit_part: str = '-')
```
**Аргументы:**
- **name : mean**
- `unit_part` (`str`): Единичная часть строкового разделителя
---
## *class* :: `Router`
Класс, который определяет и конфигурирует обработчики команд
### Конструктор
```python
Router(title: str | None = None,
name: str = 'Default')
```
**Аргументы:**
- **name : mean**
- `title` (`str`): Заголовок группы команд.
- `name` (`str`): Персональное название роутера
---
### ***methods***
---
#### **command(command: Command)**
*param* `command: Command` **::** Экземпляр класса `Command`, который определяет строковый триггер команды,
допустимые флаги команды и другое
*required* **::** True
*example* **::** `Command(command='ssh', description='connect via ssh')`
*method mean* **::** Декоратор, который регистрирует функцию как обработчик команды
---
#### **.get_name() -> `str`**
*method mean* **::** Возвращает установленное название роутера
---
#### **.get_title() -> `str`**
*method mean* **::** Возвращает установленный заголовок группы команд данного роутера
---
### Исключения
- `RepeatedFlagNameException` - Повторяющиеся зарегистрированные флаги в команде
- `TooManyTransferredArgsException` - Слишком много зарегистрированных аргументов у обработчика команды
- `RequiredArgumentNotPassedException` - Не зарегистрирован обязательный аргумент у обработчика команды(аргумент, через который будут переданы флаги введённой команды)
- `IncorrectNumberOfHandlerArgsException` - У обработчика нестандартного поведения зарегистрировано неверное количество аргументов(в большинстве случаев у него должен быть один аргумент)
- `TriggerCannotContainSpacesException` - У регистрируемой команды в триггере содержатся пробелы
---
## *class* :: `Command`
Класс, экземпляр которого определяет строковый триггер хэндлера и конфигурирует его атрибуты
### Конструктор
```python
Command(trigger: str,
description: str = None,
flags: Flag | Flags = None)
```
**Аргументы:**
- **name : mean**
- `trigger` (`str`): Строковый триггер
- `description` (`str`): Описание команды, которое будет выведено в консоль при запуске оболочки
- `flags` (`Flag | Flags`): Флаги, которые будут обработаны при их наличии во вводе юзера
---
#### **.get_trigger() -> `str`**
*method mean* **::** Возвращает строковый триггер экземпляра
---
#### **.get_description() -> `str`**
*method mean* **::** Возвращает описание команды
---
#### **.get_registered_flags() -> `Flags | None`**
*method mean* **::** Возвращает зарегистрированные флаги экземпляра
---
### Исключения
- `UnprocessedInputFlagException` - Некорректный синтаксис ввода команды
- `RepeatedInputFlagsException` - Повторяющиеся флаги во введённой команде
- `EmptyInputCommandException` - Введённая команда является пустой(не содержит символов)
**Примечание**
Все вышеуказанные исключения класса `Command` вызываются в рантайме запущенным экземпляром класса
`App`, а также по дефолту обрабатываются, при желании можно задать пользовательские
обработчики для этих исключений ([подробнее см.](#custom_handler))
---
## *class* :: `Flag`
Класс, экземпляры которого в большинстве случаев передаются при создании
экземпляра класса `Command` для регистрации допустимого флага при вводе юзером команды
### Конструктор
```python
Flag(name: str,
prefix: typing.Literal['-', '--', '---'] = '-',
possible_values: list[str] | typing.Pattern[str] | False = True)
```
---
**Аргументы:**
- **name : mean**
- `name` (`str`): Имя флага
- `prefix` (`Literal['-', '--', '---']`): Префикс команды, допустимым значением является от одного до трёх минусов
- `possible_values` (`list[str] | Pattern[str] | bool`): Множество допустимых значений флага, может быть задано
списком с допустимыми значениями или регулярным выражением (рекомендуется `re.compile(r'example exspression')`), при значении
аргумента `False` у введённого флага не может быть значения, иначе будет вызвано исключение и обработано соответствующим
еррор-хэндлером
---
### ***methods***
---
#### **.get_string_entity() -> `str`**
*method mean* **::** Возвращает строковое представление флага(префикс + имя)
---
#### **.get_name() -> `str`**
*method mean* **::** Возвращает имя флага
---
#### **.get_prefix() -> `str`**
*method mean* **::** Возвращает префикс флага
---
## *class* :: `InputFlag`
Класс, экземпляры которого являются введёнными флагами команды, передаётся в хэндлер команды
через `InputFlags`
---
### Примечания
- Наиболее часто используемые флаги предопределены и доступны для быстрого использования:
`argenta.command.flag.defaults.PredeterminedFlags`
---
### Конструктор
```python
InputFlag(name: str,
prefix: typing.Literal['-', '--', '---'] = '-',
value: str = None)
```
---
**Аргументы:**
- **name : mean**
- `name` (`str`): Имя флага
- `prefix` (`Literal['-', '--', '---']`): Префикс команды, допустимым значением является от одного до трёх минусов
- `value` (`str`): Значение введённого флага, если оно есть
---
### ***methods***
---
#### **.get_value() -> `str | None`**
*method mean* **::** Возвращает значение введённого флага
---
## *class* :: `Flags`
Класс, объединяющий список флагов в один объект, используется в качестве
передаваемого аргумента `flags` экземпляру класса `Command`, при регистрации
хэндлера
### Конструктор
```python
Flags(*flags: Flag)
```
---
**Аргументы:**
- **name : mean**
- `*flags` (`Flag`): Неограниченное количество передаваемых флагов
---
### ***methods***
---
#### **.get_flags() -> `list[Flag]`**
*method mean* **::** Возвращает зарегистрированные флаги
---
#### **.add_flag(flag: Flag) -> `None`**
*method mean* **::** Добавляет флаг в группу
---
#### **.add_flags(flags: list[Flag]) -> `None`**
*method mean* **::** Добавляет флаги в группу
---
#### **.get_flag(name: str) -> `Flag | None`**
*param* `name: str` **::** Строковый триггер флага без префикса
*required* **::** True
*example* **::** `'host'`
*method mean* **::** Возвращает флаг по его триггеру или `None`, если флаг не найден
---
## *class* :: `InputFlags`
Класс, объединяющий список введённых флагов в один объект, передаётся соответствующему хэндлеру
в качестве аргумента
### Конструктор
```python
InputFlags(*flags: Flag)
```
---
**Аргументы:**
- **name : mean**
- `*flags` (`InputFlag`): Неограниченное количество передаваемых флагов
---
### ***methods***
---
#### **.get_flags() -> `list[Flag]`**
*method mean* **::** Возвращает введённые флаги
---
#### **.get_flag(name: str) -> `InputFlag | None`**
*param* `name: str` **::** Строковый триггер флага без префикса
*required* **::** True
*example* **::** `'host'`
*method mean* **::** Возвращает введённый флаг по его триггеру или `None`, если флаг не найден
---
# Тесты
Запуск тестов:
```bash
python -m unittest discover
```
or
```bash
python -m unittest discover -v
```
+69
View File
@@ -0,0 +1,69 @@
# Argenta
### Библиотека для создания модульных CLI приложeний
Argenta позволяет инкапсулировать CLI фукциональность в изолированные, абстрагированные **среды**. К примеру: вы создаете утилиту, подобную Metasploit Framework, где пользователь сначала входит в определенный скоуп (например, выбирает модуль для сканирования), а затем получает доступ к набору команд, специфичных только для этого контекста. Argenta предоставляет простой и лаконичный способ для построения такой архитектуры.
---
![preview](https://github.com/koloideal/Argenta/blob/main/imgs/mock_app_preview4.png?raw=True)
---
# Установка
```bash
pip install argenta
```
or
```bash
poetry add argenta
```
---
# Быстрый старт
Пример простейшего приложения
```python
# routers.py
from argenta.router import Router
from argenta.command import Command
from argenta.response import Response
router = Router()
@router.command(Command("hello"))
def handler(response: Response):
print("Hello, world!")
```
```python
# main.py
from argenta.app import App
from argenta.orchestrator import Orchestrator
from routers import router
app: App = App()
orchestrator: Orchestrator = Orchestrator()
def main() -> None:
app.include_router(router)
orchestrator.start_polling(app)
if __name__ == '__main__':
main()
```
---
# Фичи в разработке
- Полноценная поддержка автокомплитера на Linux
## Полная [документация](https://argenta-docs.vercel.app) | MIT 2025 kolo | made by [kolo](https://t.me/kolo_id)
-3
View File
@@ -1,3 +0,0 @@
__all__ = ["App"]
from argenta.app.models import App
-47
View File
@@ -1,47 +0,0 @@
import os
import readline
class AutoCompleter:
def __init__(self, history_filename: str = False, autocomplete_button: str = 'tab'):
self.history_filename = history_filename
self.autocomplete_button = autocomplete_button
self.matches = []
def complete(self, text, state):
matches = sorted(cmd for cmd in self.get_history_items() if cmd.startswith(text))
if len(matches) > 1:
common_prefix = matches[0]
for match in matches[1:]:
i = 0
while i < len(common_prefix) and i < len(match) and common_prefix[i] == match[i]:
i += 1
common_prefix = common_prefix[:i]
if state == 0:
readline.insert_text(common_prefix[len(text):])
readline.redisplay()
return None
elif len(matches) == 1:
return matches[0] if state == 0 else None
else:
return None
def initial_setup(self, all_commands: list[str]):
if self.history_filename:
if os.path.exists(self.history_filename):
readline.read_history_file(self.history_filename)
else:
for line in all_commands:
readline.add_history(line)
readline.set_completer(self.complete)
readline.set_completer_delims(readline.get_completer_delims().replace(' ', ''))
readline.parse_and_bind(f'{self.autocomplete_button}: complete')
def exit_setup(self):
if self.history_filename:
readline.write_history_file(self.history_filename)
@staticmethod
def get_history_items():
return [readline.get_history_item(i) for i in range(1, readline.get_current_history_length() + 1)]
-9
View File
@@ -1,9 +0,0 @@
from dataclasses import dataclass
@dataclass
class PredeterminedMessages:
USAGE = '[b dim]Usage[/b dim]: [i]<command> <[green]flags[/green]>[/i]'
HELP = '[b dim]Help[/b dim]: [i]<command>[/i] [b red]--help[/b red]'
AUTOCOMPLETE = '[b dim]Autocomplete[/b dim]: [i]<part>[/i] [bold]<tab>'
-24
View File
@@ -1,24 +0,0 @@
class BaseDividingLine:
def __init__(self, unit_part: str = '-'):
self.unit_part = unit_part
def get_unit_part(self):
if len(self.unit_part) == 0:
return ' '
else:
return self.unit_part[0]
class StaticDividingLine(BaseDividingLine):
def __init__(self, unit_part: str = '-', length: int = 25):
super().__init__(unit_part)
self.length = length
def get_full_line(self):
return f'\n[dim]{self.length * self.get_unit_part()}[/dim]\n'
class DynamicDividingLine(BaseDividingLine):
def get_full_line(self, length: int):
return f'\n[dim]{self.get_unit_part() * length}[/dim]\n'
-10
View File
@@ -1,10 +0,0 @@
class NoRegisteredRoutersException(Exception):
def __str__(self):
return "No Registered Router Found"
class NoRegisteredHandlersException(Exception):
def __init__(self, router_name):
self.router_name = router_name
def __str__(self):
return f"No Registered Handlers Found For '{self.router_name}'"
-265
View File
@@ -1,265 +0,0 @@
from typing import Callable
from rich.console import Console
from rich.markup import escape
from art import text2art
from contextlib import redirect_stdout
import io
import re
from argenta.command.models import Command, InputCommand
from argenta.router import Router
from argenta.router.defaults import system_router
from argenta.app.autocompleter import AutoCompleter
from argenta.app.dividing_line.models import StaticDividingLine, DynamicDividingLine
from argenta.command.exceptions import (UnprocessedInputFlagException,
RepeatedInputFlagsException,
EmptyInputCommandException,
BaseInputCommandException)
from argenta.app.exceptions import (NoRegisteredRoutersException,
NoRegisteredHandlersException)
from argenta.app.registered_routers.entity import RegisteredRouters
class AppInit:
def __init__(self,
prompt: str = '[italic dim bold]What do you want to do?\n',
initial_message: str = '\nArgenta\n',
farewell_message: str = '\nSee you\n',
exit_command: Command = Command('Q', 'Exit command'),
system_points_title: str | None = 'System points:',
ignore_command_register: bool = True,
dividing_line: StaticDividingLine | DynamicDividingLine = StaticDividingLine(),
repeat_command_groups: bool = True,
override_system_messages: bool = False,
autocompleter: AutoCompleter = AutoCompleter(),
print_func: Callable[[str], None] = Console().print) -> None:
self._prompt = prompt
self._print_func = print_func
self._exit_command = exit_command
self._system_points_title = system_points_title
self._dividing_line = dividing_line
self._ignore_command_register = ignore_command_register
self._repeat_command_groups_description = repeat_command_groups
self._override_system_messages = override_system_messages
self._autocompleter = autocompleter
self._farewell_message = farewell_message
self._initial_message = initial_message
self._description_message_gen: Callable[[str, str], str] = lambda command, description: f'[bold red]{escape('['+command+']')}[/bold red] [blue dim]*=*=*[/blue dim] [bold yellow italic]{escape(description)}'
self._registered_routers: RegisteredRouters = RegisteredRouters()
self._messages_on_startup = []
self._invalid_input_flags_handler: Callable[[str], None] = lambda raw_command: print_func(f'[red bold]Incorrect flag syntax: {escape(raw_command)}')
self._repeated_input_flags_handler: Callable[[str], None] = lambda raw_command: print_func(f'[red bold]Repeated input flags: {escape(raw_command)}')
self._empty_input_command_handler: Callable[[], None] = lambda: print_func('[red bold]Empty input command')
self._unknown_command_handler: Callable[[InputCommand], None] = lambda command: print_func(f"[red bold]Unknown command: {escape(command.get_trigger())}")
self._exit_command_handler: Callable[[], None] = lambda: print_func(self._farewell_message)
class AppSetters(AppInit):
def set_description_message_pattern(self, pattern: Callable[[str, str], str]) -> None:
self._description_message_gen: Callable[[str, str], str] = pattern
def set_invalid_input_flags_handler(self, handler: Callable[[str], None]) -> None:
self._invalid_input_flags_handler = handler
def set_repeated_input_flags_handler(self, handler: Callable[[str], None]) -> None:
self._repeated_input_flags_handler = handler
def set_unknown_command_handler(self, handler: Callable[[str], None]) -> None:
self._unknown_command_handler = handler
def set_empty_command_handler(self, handler: Callable[[], None]) -> None:
self._empty_input_command_handler = handler
def set_exit_command_handler(self, handler: Callable[[], None]) -> None:
self._exit_command_handler = handler
class AppPrinters(AppInit):
def _print_command_group_description(self):
for registered_router in self._registered_routers:
if registered_router.get_title():
self._print_func(registered_router.get_title())
for command_handler in registered_router.get_command_handlers():
self._print_func(self._description_message_gen(
command_handler.get_handled_command().get_trigger(),
command_handler.get_handled_command().get_description()))
self._print_func('')
def _print_framed_text_with_dynamic_line(self, text: str):
clear_text = re.sub(r'\u001b\[[0-9;]*m', '', text)
max_length_line = max([len(line) for line in clear_text.split('\n')])
max_length_line = max_length_line if 10 <= max_length_line <= 80 else 80 if max_length_line > 80 else 10
self._print_func(self._dividing_line.get_full_line(max_length_line))
print(text.strip('\n'))
self._print_func(self._dividing_line.get_full_line(max_length_line))
def _print_framed_text(self, text: str):
if isinstance(self._dividing_line, StaticDividingLine):
self._print_func(self._dividing_line.get_full_line())
self._print_func(text)
self._print_func(self._dividing_line.get_full_line())
elif isinstance(self._dividing_line, DynamicDividingLine):
self._print_framed_text_with_dynamic_line(text)
class AppNonStandardHandlers(AppPrinters):
def _is_exit_command(self, command: InputCommand):
if command.get_trigger().lower() == self._exit_command.get_trigger().lower():
if self._ignore_command_register:
system_router.input_command_handler(command)
return True
elif command.get_trigger() == self._exit_command.get_trigger():
system_router.input_command_handler(command)
return True
return False
def _is_unknown_command(self, command: InputCommand):
for router_entity in self._registered_routers:
for command_handler in router_entity.get_command_handlers():
handled_command_trigger = command_handler.get_handled_command().get_trigger()
handled_command_aliases = command_handler.get_handled_command().get_aliases()
if handled_command_trigger.lower() == command.get_trigger().lower() and self._ignore_command_register:
return False
elif handled_command_trigger == command.get_trigger():
return False
elif handled_command_aliases:
if (command.get_trigger().lower() in [x.lower() for x in handled_command_aliases]) and self._ignore_command_register:
return False
elif command.get_trigger() in handled_command_trigger:
return False
if isinstance(self._dividing_line, StaticDividingLine):
self._print_func(self._dividing_line.get_full_line())
self._unknown_command_handler(command)
self._print_func(self._dividing_line.get_full_line())
elif isinstance(self._dividing_line, DynamicDividingLine):
with redirect_stdout(io.StringIO()) as f:
self._unknown_command_handler(command)
res: str = f.getvalue()
self._print_framed_text_with_dynamic_line(res)
return True
def _error_handler(self, error: BaseInputCommandException, raw_command: str) -> None:
match error:
case UnprocessedInputFlagException():
self._invalid_input_flags_handler(raw_command)
case RepeatedInputFlagsException():
self._repeated_input_flags_handler(raw_command)
case EmptyInputCommandException():
self._empty_input_command_handler()
class AppValidators(AppInit):
def _validate_number_of_routers(self) -> None:
if not self._registered_routers:
raise NoRegisteredRoutersException()
def _validate_included_routers(self) -> None:
for router in self._registered_routers:
if not router.get_command_handlers():
raise NoRegisteredHandlersException(router.get_name())
class AppSetups(AppValidators, AppPrinters):
def _setup_system_router(self):
system_router.set_title(self._system_points_title)
@system_router.command(self._exit_command)
def exit_command():
self._exit_command_handler()
if system_router not in self._registered_routers.get_registered_routers():
system_router.set_ignore_command_register(self._ignore_command_register)
self._registered_routers.add_registered_router(system_router)
def _setup_default_view(self):
if not self._override_system_messages:
self._initial_message = f'\n[bold red]{text2art(self._initial_message, font='tarty1')}\n\n'
self._farewell_message = (
f'[bold red]\n{text2art(f'\n{self._farewell_message}\n', font='chanky')}[/bold red]\n'
f'[red i]github.com/koloideal/Argenta[/red i] | [red bold i]made by kolo[/red bold i]\n')
def _pre_cycle_setup(self):
self._setup_default_view()
self._setup_system_router()
self._validate_number_of_routers()
self._validate_included_routers()
all_triggers: list[str] = []
for router_entity in self._registered_routers:
all_triggers.extend(router_entity.get_triggers())
all_triggers.extend(router_entity.get_aliases())
self._autocompleter.initial_setup(all_triggers)
self._print_func(self._initial_message)
for message in self._messages_on_startup:
self._print_func(message)
print('\n\n')
if not self._repeat_command_groups_description:
self._print_command_group_description()
class App(AppSetters, AppNonStandardHandlers, AppSetups):
def start_polling(self) -> None:
self._pre_cycle_setup()
while True:
if self._repeat_command_groups_description:
self._print_command_group_description()
raw_command: str = Console().input(self._prompt)
try:
input_command: InputCommand = InputCommand.parse(raw_command=raw_command)
except BaseInputCommandException as error:
with redirect_stdout(io.StringIO()) as f:
self._error_handler(error, raw_command)
res: str = f.getvalue()
self._print_framed_text(res)
continue
if self._is_exit_command(input_command):
self._autocompleter.exit_setup()
return
if self._is_unknown_command(input_command):
continue
with redirect_stdout(io.StringIO()) as f:
for registered_router in self._registered_routers:
registered_router.input_command_handler(input_command)
res: str = f.getvalue()
self._print_framed_text(res)
if not self._repeat_command_groups_description:
self._print_func(self._prompt)
def include_router(self, router: Router) -> None:
router.set_ignore_command_register(self._ignore_command_register)
self._registered_routers.add_registered_router(router)
def include_routers(self, *routers: Router) -> None:
for router in routers:
self.include_router(router)
def add_message_on_startup(self, message: str) -> None:
self._messages_on_startup.append(message)
-21
View File
@@ -1,21 +0,0 @@
from argenta.router import Router
class RegisteredRouters:
def __init__(self, registered_routers: list[Router] = None) -> None:
self._registered_routers = registered_routers if registered_routers else []
def get_registered_routers(self) -> list[Router]:
return self._registered_routers
def add_registered_router(self, router: Router):
self._registered_routers.append(router)
def add_registered_routers(self, *routers: Router):
self._registered_routers.extend(routers)
def __iter__(self):
return iter(self._registered_routers)
def __next__(self):
return next(iter(self._registered_routers))
-3
View File
@@ -1,3 +0,0 @@
__all__ = ["Command"]
from argenta.command.models import Command
-23
View File
@@ -1,23 +0,0 @@
from argenta.command.flag.models import InputFlag, Flag
class BaseInputCommandException(Exception):
pass
class UnprocessedInputFlagException(BaseInputCommandException):
def __str__(self):
return "Unprocessed Input Flags"
class RepeatedInputFlagsException(BaseInputCommandException):
def __init__(self, flag: Flag | InputFlag):
self.flag = flag
def __str__(self):
return ("Repeated Input Flags\n"
f"Duplicate flag was detected in the input: '{self.flag.get_string_entity()}'")
class EmptyInputCommandException(BaseInputCommandException):
def __str__(self):
return "Input Command is empty"
-4
View File
@@ -1,4 +0,0 @@
__all__ = ('InputFlags', 'InputFlag', 'Flag', 'Flags')
from argenta.command.flag.models import InputFlags, InputFlag, Flags, Flag
-21
View File
@@ -1,21 +0,0 @@
from dataclasses import dataclass
from argenta.command.flag.models import Flag
import re
@dataclass
class PredeterminedFlags:
HELP = Flag(name='help', possible_values=False)
SHORT_HELP = Flag(name='h', prefix='-', possible_values=False)
INFO = Flag(name='info', possible_values=False)
SHORT_INFO = Flag(name='i', prefix='-', possible_values=False)
ALL = Flag(name='all', possible_values=False)
SHORT_ALL = Flag(name='a', prefix='-', possible_values=False)
HOST = Flag(name='host', possible_values=re.compile(r'^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$'))
SHORT_HOST = Flag(name='h', prefix='-', possible_values=re.compile(r'^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$'))
PORT = Flag(name='port', possible_values=re.compile(r'^\d{1,5}$'))
SHORT_PORT = Flag(name='p', prefix='-', possible_values=re.compile(r'^\d{1,5}$'))
-140
View File
@@ -1,140 +0,0 @@
from typing import Literal, Pattern
from abc import ABC, abstractmethod
class BaseFlag:
def __init__(self, name: str,
prefix: Literal['-', '--', '---'] = '--'):
self._name = name
self._prefix = prefix
def get_string_entity(self):
string_entity: str = self._prefix + self._name
return string_entity
def get_name(self):
return self._name
def get_prefix(self):
return self._prefix
class InputFlag(BaseFlag):
def __init__(self, name: str,
prefix: Literal['-', '--', '---'] = '--',
value: str = None):
super().__init__(name, prefix)
self._flag_value = value
def get_value(self) -> str | None:
return self._flag_value
def set_value(self, value):
self._flag_value = value
class Flag(BaseFlag):
def __init__(self, name: str,
prefix: Literal['-', '--', '---'] = '--',
possible_values: list[str] | Pattern[str] | False = True):
super().__init__(name, prefix)
self.possible_values = possible_values
def validate_input_flag_value(self, input_flag_value: str | None):
if self.possible_values is False:
if input_flag_value is None:
return True
else:
return False
elif isinstance(self.possible_values, Pattern):
if isinstance(input_flag_value, str):
is_valid = bool(self.possible_values.match(input_flag_value))
if bool(is_valid):
return True
else:
return False
else:
return False
elif isinstance(self.possible_values, list):
if input_flag_value in self.possible_values:
return True
else:
return False
else:
return True
class BaseFlags(ABC):
__slots__ = ('_flags',)
@abstractmethod
def get_flags(self):
pass
@abstractmethod
def add_flag(self, flag: Flag | InputFlag):
pass
@abstractmethod
def add_flags(self, flags: list[Flag] | list[InputFlag]):
pass
@abstractmethod
def get_flag(self, name: str):
pass
def __iter__(self):
return iter(self._flags)
def __next__(self):
return next(iter(self))
def __getitem__(self, item):
return self._flags[item]
class Flags(BaseFlags, ABC):
def __init__(self, *flags: Flag):
self._flags = flags if flags else []
def get_flags(self) -> list[Flag]:
return self._flags
def add_flag(self, flag: Flag):
self._flags.append(flag)
def add_flags(self, flags: list[Flag]):
self._flags.extend(flags)
def get_flag(self, name: str) -> Flag | None:
if name in [flag.get_name() for flag in self._flags]:
return list(filter(lambda flag: flag.get_name() == name, self._flags))[0]
else:
return None
class InputFlags(BaseFlags, ABC):
def __init__(self, *flags: InputFlag):
self._flags = flags if flags else []
def get_flags(self) -> list[InputFlag]:
return self._flags
def add_flag(self, flag: InputFlag):
self._flags.append(flag)
def add_flags(self, flags: list[InputFlag]):
self._flags.extend(flags)
def get_flag(self, name: str) -> InputFlag | None:
if name in [flag.get_name() for flag in self._flags]:
return list(filter(lambda flag: flag.get_name() == name, self._flags))[0]
else:
return None
-112
View File
@@ -1,112 +0,0 @@
from argenta.command.flag.models import Flag, InputFlag, Flags, InputFlags
from argenta.command.exceptions import (UnprocessedInputFlagException,
RepeatedInputFlagsException,
EmptyInputCommandException)
from typing import Generic, TypeVar, cast, Literal
InputCommandType = TypeVar('InputCommandType')
class BaseCommand:
def __init__(self, trigger: str):
self._trigger = trigger
def get_trigger(self) -> str:
return self._trigger
class Command(BaseCommand):
def __init__(self, trigger: str,
description: str = None,
flags: Flag | Flags = None,
aliases: list[str] = None):
super().__init__(trigger)
self._registered_flags: Flags = flags if isinstance(flags, Flags) else Flags(flags) if isinstance(flags, Flag) else Flags()
self._description = f'Description for "{self._trigger}" command' if not description else description
self._aliases = aliases
def get_registered_flags(self) -> Flags:
return self._registered_flags
def get_aliases(self) -> list[str] | None:
return self._aliases
def validate_input_flag(self, flag: InputFlag):
registered_flags: Flags | None = self.get_registered_flags()
if registered_flags:
if isinstance(registered_flags, Flag):
if registered_flags.get_string_entity() == flag.get_string_entity():
is_valid = registered_flags.validate_input_flag_value(flag.get_value())
if is_valid:
return True
else:
for registered_flag in registered_flags:
if registered_flag.get_string_entity() == flag.get_string_entity():
is_valid = registered_flag.validate_input_flag_value(flag.get_value())
if is_valid:
return True
return False
def get_description(self) -> str:
return self._description
class InputCommand(BaseCommand, Generic[InputCommandType]):
def __init__(self, trigger: str,
input_flags: InputFlag | InputFlags = None):
super().__init__(trigger)
self._input_flags: InputFlags = input_flags if isinstance(input_flags, InputFlags) else InputFlags(input_flags) if isinstance(input_flags, InputFlag) else InputFlags()
def _set_input_flags(self, input_flags: InputFlags):
self._input_flags = input_flags
def get_input_flags(self) -> InputFlags:
return self._input_flags
@staticmethod
def parse(raw_command: str) -> InputCommandType:
if not raw_command:
raise EmptyInputCommandException()
list_of_tokens = raw_command.split()
command = list_of_tokens.pop(0)
input_flags: InputFlags = InputFlags()
current_flag_name, current_flag_value = None, None
for k, _ in enumerate(list_of_tokens):
if _.startswith('-'):
if current_flag_name or len(_) < 2 or len(_[:_.rfind('-')]) > 3:
raise UnprocessedInputFlagException()
current_flag_name = _
else:
if not current_flag_name:
raise UnprocessedInputFlagException()
current_flag_value = _
if current_flag_name:
if not len(list_of_tokens) == k+1:
if not list_of_tokens[k+1].startswith('-'):
continue
input_flag = InputFlag(name=current_flag_name[current_flag_name.rfind('-')+1:],
prefix=cast(Literal['-', '--', '---'],
current_flag_name[:current_flag_name.rfind('-')+1]),
value=current_flag_value)
all_flags = [flag.get_string_entity() for flag in input_flags.get_flags()]
if input_flag.get_string_entity() not in all_flags:
input_flags.add_flag(input_flag)
else:
raise RepeatedInputFlagsException(input_flag)
current_flag_name, current_flag_value = None, None
if any([current_flag_name, current_flag_value]):
raise UnprocessedInputFlagException()
else:
return InputCommand(trigger=command, input_flags=input_flags)
-4
View File
@@ -1,4 +0,0 @@
__all__ = ["Router"]
from argenta.router.entity import Router
-21
View File
@@ -1,21 +0,0 @@
from typing import Callable
from argenta.command import Command
from argenta.command.flag.models import InputFlags
class CommandHandler:
def __init__(self, handler: Callable[[], None] | Callable[[InputFlags], None], handled_command: Command):
self._handler = handler
self._handled_command = handled_command
def handling(self, input_flags: InputFlags = None):
if input_flags is not None:
self._handler(input_flags)
else:
self._handler()
def get_handler(self):
return self._handler
def get_handled_command(self):
return self._handled_command
-21
View File
@@ -1,21 +0,0 @@
from argenta.router.command_handler.entity import CommandHandler
class CommandHandlers:
def __init__(self, command_handlers: list[CommandHandler] = None):
self.command_handlers = command_handlers if command_handlers else []
def get_command_handlers(self) -> list[CommandHandler]:
return self.command_handlers
def add_command_handler(self, command_handler: CommandHandler):
self.command_handlers.append(command_handler)
def add_command_handlers(self, *command_handlers: CommandHandler):
self.command_handlers.extend(command_handlers)
def __iter__(self):
return iter(self.command_handlers)
def __next__(self):
return next(iter(self.command_handlers))
-5
View File
@@ -1,5 +0,0 @@
from argenta.router import Router
system_router = Router(title='System points:',
name='System')
-149
View File
@@ -1,149 +0,0 @@
from typing import Callable, Any
from inspect import getfullargspec
from argenta.command import Command
from argenta.command.models import InputCommand
from argenta.router.command_handlers.entity import CommandHandlers
from argenta.router.command_handler.entity import CommandHandler
from argenta.command.flag.models import Flag, Flags, InputFlags
from argenta.router.exceptions import (RepeatedFlagNameException,
TooManyTransferredArgsException,
RequiredArgumentNotPassedException,
IncorrectNumberOfHandlerArgsException,
TriggerCannotContainSpacesException)
class Router:
def __init__(self,
title: str = None,
name: str = 'Default'):
self._title = title
self._name = name
self._command_handlers: CommandHandlers = CommandHandlers()
self._ignore_command_register: bool = False
self._not_valid_flag_handler: Callable[[Flag], None] = lambda flag: print(f"Undefined or incorrect input flag: {flag.get_string_entity()}{(' '+flag.get_value()) if flag.get_value() else ''}")
def command(self, command: Command) -> Callable[[Any], Any]:
self._validate_command(command)
def command_decorator(func):
Router._validate_func_args(command, func)
self._command_handlers.add_command_handler(CommandHandler(func, command))
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
return command_decorator
def set_invalid_input_flag_handler(self, func):
processed_args = getfullargspec(func).args
if len(processed_args) != 1:
raise IncorrectNumberOfHandlerArgsException()
else:
self._not_valid_flag_handler = func
def input_command_handler(self, input_command: InputCommand):
input_command_name: str = input_command.get_trigger()
input_command_flags: InputFlags = input_command.get_input_flags()
for command_handler in self._command_handlers:
handle_command = command_handler.get_handled_command()
if input_command_name.lower() == handle_command.get_trigger().lower():
self._validate_input_command(input_command_flags, command_handler)
elif handle_command.get_aliases():
if input_command_name.lower() in handle_command.get_aliases():
self._validate_input_command(input_command_flags, command_handler)
def _validate_input_command(self, input_command_flags: InputFlags, command_handler: CommandHandler):
handle_command = command_handler.get_handled_command()
if handle_command.get_registered_flags().get_flags():
if input_command_flags.get_flags():
if self._validate_input_flags(handle_command, input_command_flags):
command_handler.handling(input_command_flags)
return
else:
command_handler.handling(input_command_flags)
return
else:
if input_command_flags.get_flags():
self._not_valid_flag_handler(input_command_flags[0])
return
else:
command_handler.handling()
return
def _validate_input_flags(self, handle_command: Command, input_flags: InputFlags):
for flag in input_flags:
is_valid = handle_command.validate_input_flag(flag)
if not is_valid:
self._not_valid_flag_handler(flag)
return False
return True
@staticmethod
def _validate_command(command: Command):
command_name: str = command.get_trigger()
if command_name.find(' ') != -1:
raise TriggerCannotContainSpacesException()
flags: Flags = command.get_registered_flags()
if flags:
flags_name: list = [x.get_string_entity().lower() for x in flags]
if len(set(flags_name)) < len(flags_name):
raise RepeatedFlagNameException()
@staticmethod
def _validate_func_args(command: Command, func: Callable):
registered_args = command.get_registered_flags()
transferred_args = getfullargspec(func).args
if registered_args.get_flags() and transferred_args:
if len(transferred_args) != 1:
raise TooManyTransferredArgsException()
elif registered_args.get_flags() and not transferred_args:
raise RequiredArgumentNotPassedException()
elif not registered_args.get_flags() and transferred_args:
raise TooManyTransferredArgsException()
def set_ignore_command_register(self, ignore_command_register: bool):
self._ignore_command_register = ignore_command_register
def get_triggers(self):
all_triggers: list[str] = []
for command_handler in self._command_handlers:
all_triggers.append(command_handler.get_handled_command().get_trigger())
return all_triggers
def get_aliases(self):
all_aliases: list[str] = []
for command_handler in self._command_handlers:
if command_handler.get_handled_command().get_aliases():
all_aliases.extend(command_handler.get_handled_command().get_aliases())
return all_aliases
def get_command_handlers(self) -> CommandHandlers:
return self._command_handlers
def get_name(self) -> str:
return self._name
def get_title(self) -> str | None:
return self._title
def set_title(self, title: str):
self._title = title
-23
View File
@@ -1,23 +0,0 @@
class RepeatedFlagNameException(Exception):
def __str__(self):
return "Repeated registered_flag name in register command"
class TooManyTransferredArgsException(Exception):
def __str__(self):
return "Too many transferred arguments"
class RequiredArgumentNotPassedException(Exception):
def __str__(self):
return "Required argument not passed"
class IncorrectNumberOfHandlerArgsException(Exception):
def __str__(self):
return "Handler has incorrect number of arguments"
class TriggerCannotContainSpacesException(Exception):
def __str__(self):
return "Command trigger cannot contain spaces"
+20
View File
@@ -0,0 +1,20 @@
# Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line, and also
# from the environment for the first two.
SPHINXOPTS ?=
SPHINXBUILD ?= sphinx-build
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
Binary file not shown.
Binary file not shown.
+4
View File
@@ -0,0 +1,4 @@
# Sphinx build info version 1
# This file records the configuration used when building these files. When it is not found, a full rebuild will be done.
config: a7676963520569074adf4bb73b4ff361
tags: 645f666f9bcd5a90fca523b33c5a78b7
+17
View File
@@ -0,0 +1,17 @@
.. Argenta documentation master file, created by
sphinx-quickstart on Sat Oct 11 19:54:43 2025.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Argenta documentation
=====================
Add your content using ``reStructuredText`` syntax. See the
`reStructuredText <https://www.sphinx-doc.org/en/master/usage/restructuredtext/index.html>`_
documentation for details.
.. toctree::
:maxdepth: 2
:caption: Contents:
@@ -0,0 +1,123 @@
/* Compatability shim for jQuery and underscores.js.
*
* Copyright Sphinx contributors
* Released under the two clause BSD licence
*/
/**
* small helper function to urldecode strings
*
* See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/decodeURIComponent#Decoding_query_parameters_from_a_URL
*/
jQuery.urldecode = function(x) {
if (!x) {
return x
}
return decodeURIComponent(x.replace(/\+/g, ' '));
};
/**
* small helper function to urlencode strings
*/
jQuery.urlencode = encodeURIComponent;
/**
* This function returns the parsed url parameters of the
* current request. Multiple values per key are supported,
* it will always return arrays of strings for the value parts.
*/
jQuery.getQueryParameters = function(s) {
if (typeof s === 'undefined')
s = document.location.search;
var parts = s.substr(s.indexOf('?') + 1).split('&');
var result = {};
for (var i = 0; i < parts.length; i++) {
var tmp = parts[i].split('=', 2);
var key = jQuery.urldecode(tmp[0]);
var value = jQuery.urldecode(tmp[1]);
if (key in result)
result[key].push(value);
else
result[key] = [value];
}
return result;
};
/**
* highlight a given string on a jquery object by wrapping it in
* span elements with the given class name.
*/
jQuery.fn.highlightText = function(text, className) {
function highlight(node, addItems) {
if (node.nodeType === 3) {
var val = node.nodeValue;
var pos = val.toLowerCase().indexOf(text);
if (pos >= 0 &&
!jQuery(node.parentNode).hasClass(className) &&
!jQuery(node.parentNode).hasClass("nohighlight")) {
var span;
var isInSVG = jQuery(node).closest("body, svg, foreignObject").is("svg");
if (isInSVG) {
span = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
} else {
span = document.createElement("span");
span.className = className;
}
span.appendChild(document.createTextNode(val.substr(pos, text.length)));
node.parentNode.insertBefore(span, node.parentNode.insertBefore(
document.createTextNode(val.substr(pos + text.length)),
node.nextSibling));
node.nodeValue = val.substr(0, pos);
if (isInSVG) {
var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
var bbox = node.parentElement.getBBox();
rect.x.baseVal.value = bbox.x;
rect.y.baseVal.value = bbox.y;
rect.width.baseVal.value = bbox.width;
rect.height.baseVal.value = bbox.height;
rect.setAttribute('class', className);
addItems.push({
"parent": node.parentNode,
"target": rect});
}
}
}
else if (!jQuery(node).is("button, select, textarea")) {
jQuery.each(node.childNodes, function() {
highlight(this, addItems);
});
}
}
var addItems = [];
var result = this.each(function() {
highlight(this, addItems);
});
for (var i = 0; i < addItems.length; ++i) {
jQuery(addItems[i].parent).before(addItems[i].target);
}
return result;
};
/*
* backward compatibility for jQuery.browser
* This will be supported until firefox bug is fixed.
*/
if (!jQuery.browser) {
jQuery.uaMatch = function(ua) {
ua = ua.toLowerCase();
var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
/(webkit)[ \/]([\w.]+)/.exec(ua) ||
/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
/(msie) ([\w.]+)/.exec(ua) ||
ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) ||
[];
return {
browser: match[ 1 ] || "",
version: match[ 2 ] || "0"
};
};
jQuery.browser = {};
jQuery.browser[jQuery.uaMatch(navigator.userAgent).browser] = true;
}
+906
View File
@@ -0,0 +1,906 @@
/*
* Sphinx stylesheet -- basic theme.
*/
/* -- main layout ----------------------------------------------------------- */
div.clearer {
clear: both;
}
div.section::after {
display: block;
content: '';
clear: left;
}
/* -- relbar ---------------------------------------------------------------- */
div.related {
width: 100%;
font-size: 90%;
}
div.related h3 {
display: none;
}
div.related ul {
margin: 0;
padding: 0 0 0 10px;
list-style: none;
}
div.related li {
display: inline;
}
div.related li.right {
float: right;
margin-right: 5px;
}
/* -- sidebar --------------------------------------------------------------- */
div.sphinxsidebarwrapper {
padding: 10px 5px 0 10px;
}
div.sphinxsidebar {
float: left;
width: 230px;
margin-left: -100%;
font-size: 90%;
word-wrap: break-word;
overflow-wrap : break-word;
}
div.sphinxsidebar ul {
list-style: none;
}
div.sphinxsidebar ul ul,
div.sphinxsidebar ul.want-points {
margin-left: 20px;
list-style: square;
}
div.sphinxsidebar ul ul {
margin-top: 0;
margin-bottom: 0;
}
div.sphinxsidebar form {
margin-top: 10px;
}
div.sphinxsidebar input {
border: 1px solid #98dbcc;
font-family: sans-serif;
font-size: 1em;
}
div.sphinxsidebar #searchbox form.search {
overflow: hidden;
}
div.sphinxsidebar #searchbox input[type="text"] {
float: left;
width: 80%;
padding: 0.25em;
box-sizing: border-box;
}
div.sphinxsidebar #searchbox input[type="submit"] {
float: left;
width: 20%;
border-left: none;
padding: 0.25em;
box-sizing: border-box;
}
img {
border: 0;
max-width: 100%;
}
/* -- search page ----------------------------------------------------------- */
ul.search {
margin-top: 10px;
}
ul.search li {
padding: 5px 0;
}
ul.search li a {
font-weight: bold;
}
ul.search li p.context {
color: #888;
margin: 2px 0 0 30px;
text-align: left;
}
ul.keywordmatches li.goodmatch a {
font-weight: bold;
}
/* -- index page ------------------------------------------------------------ */
table.contentstable {
width: 90%;
margin-left: auto;
margin-right: auto;
}
table.contentstable p.biglink {
line-height: 150%;
}
a.biglink {
font-size: 1.3em;
}
span.linkdescr {
font-style: italic;
padding-top: 5px;
font-size: 90%;
}
/* -- general index --------------------------------------------------------- */
table.indextable {
width: 100%;
}
table.indextable td {
text-align: left;
vertical-align: top;
}
table.indextable ul {
margin-top: 0;
margin-bottom: 0;
list-style-type: none;
}
table.indextable > tbody > tr > td > ul {
padding-left: 0em;
}
table.indextable tr.pcap {
height: 10px;
}
table.indextable tr.cap {
margin-top: 10px;
background-color: #f2f2f2;
}
img.toggler {
margin-right: 3px;
margin-top: 3px;
cursor: pointer;
}
div.modindex-jumpbox {
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
margin: 1em 0 1em 0;
padding: 0.4em;
}
div.genindex-jumpbox {
border-top: 1px solid #ddd;
border-bottom: 1px solid #ddd;
margin: 1em 0 1em 0;
padding: 0.4em;
}
/* -- domain module index --------------------------------------------------- */
table.modindextable td {
padding: 2px;
border-collapse: collapse;
}
/* -- general body styles --------------------------------------------------- */
div.body {
min-width: 360px;
max-width: 800px;
}
div.body p, div.body dd, div.body li, div.body blockquote {
-moz-hyphens: auto;
-ms-hyphens: auto;
-webkit-hyphens: auto;
hyphens: auto;
}
a.headerlink {
visibility: hidden;
}
a:visited {
color: #551A8B;
}
h1:hover > a.headerlink,
h2:hover > a.headerlink,
h3:hover > a.headerlink,
h4:hover > a.headerlink,
h5:hover > a.headerlink,
h6:hover > a.headerlink,
dt:hover > a.headerlink,
caption:hover > a.headerlink,
p.caption:hover > a.headerlink,
div.code-block-caption:hover > a.headerlink {
visibility: visible;
}
div.body p.caption {
text-align: inherit;
}
div.body td {
text-align: left;
}
.first {
margin-top: 0 !important;
}
p.rubric {
margin-top: 30px;
font-weight: bold;
}
img.align-left, figure.align-left, .figure.align-left, object.align-left {
clear: left;
float: left;
margin-right: 1em;
}
img.align-right, figure.align-right, .figure.align-right, object.align-right {
clear: right;
float: right;
margin-left: 1em;
}
img.align-center, figure.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
img.align-default, figure.align-default, .figure.align-default {
display: block;
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left;
}
.align-center {
text-align: center;
}
.align-default {
text-align: center;
}
.align-right {
text-align: right;
}
/* -- sidebars -------------------------------------------------------------- */
div.sidebar,
aside.sidebar {
margin: 0 0 0.5em 1em;
border: 1px solid #ddb;
padding: 7px;
background-color: #ffe;
width: 40%;
float: right;
clear: right;
overflow-x: auto;
}
p.sidebar-title {
font-weight: bold;
}
nav.contents,
aside.topic,
div.admonition, div.topic, blockquote {
clear: left;
}
/* -- topics ---------------------------------------------------------------- */
nav.contents,
aside.topic,
div.topic {
border: 1px solid #ccc;
padding: 7px;
margin: 10px 0 10px 0;
}
p.topic-title {
font-size: 1.1em;
font-weight: bold;
margin-top: 10px;
}
/* -- admonitions ----------------------------------------------------------- */
div.admonition {
margin-top: 10px;
margin-bottom: 10px;
padding: 7px;
}
div.admonition dt {
font-weight: bold;
}
p.admonition-title {
margin: 0px 10px 5px 0px;
font-weight: bold;
}
div.body p.centered {
text-align: center;
margin-top: 25px;
}
/* -- content of sidebars/topics/admonitions -------------------------------- */
div.sidebar > :last-child,
aside.sidebar > :last-child,
nav.contents > :last-child,
aside.topic > :last-child,
div.topic > :last-child,
div.admonition > :last-child {
margin-bottom: 0;
}
div.sidebar::after,
aside.sidebar::after,
nav.contents::after,
aside.topic::after,
div.topic::after,
div.admonition::after,
blockquote::after {
display: block;
content: '';
clear: both;
}
/* -- tables ---------------------------------------------------------------- */
table.docutils {
margin-top: 10px;
margin-bottom: 10px;
border: 0;
border-collapse: collapse;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
table.align-default {
margin-left: auto;
margin-right: auto;
}
table caption span.caption-number {
font-style: italic;
}
table caption span.caption-text {
}
table.docutils td, table.docutils th {
padding: 1px 8px 1px 5px;
border-top: 0;
border-left: 0;
border-right: 0;
border-bottom: 1px solid #aaa;
}
th {
text-align: left;
padding-right: 5px;
}
table.citation {
border-left: solid 1px gray;
margin-left: 1px;
}
table.citation td {
border-bottom: none;
}
th > :first-child,
td > :first-child {
margin-top: 0px;
}
th > :last-child,
td > :last-child {
margin-bottom: 0px;
}
/* -- figures --------------------------------------------------------------- */
div.figure, figure {
margin: 0.5em;
padding: 0.5em;
}
div.figure p.caption, figcaption {
padding: 0.3em;
}
div.figure p.caption span.caption-number,
figcaption span.caption-number {
font-style: italic;
}
div.figure p.caption span.caption-text,
figcaption span.caption-text {
}
/* -- field list styles ----------------------------------------------------- */
table.field-list td, table.field-list th {
border: 0 !important;
}
.field-list ul {
margin: 0;
padding-left: 1em;
}
.field-list p {
margin: 0;
}
.field-name {
-moz-hyphens: manual;
-ms-hyphens: manual;
-webkit-hyphens: manual;
hyphens: manual;
}
/* -- hlist styles ---------------------------------------------------------- */
table.hlist {
margin: 1em 0;
}
table.hlist td {
vertical-align: top;
}
/* -- object description styles --------------------------------------------- */
.sig {
font-family: 'Consolas', 'Menlo', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', monospace;
}
.sig-name, code.descname {
background-color: transparent;
font-weight: bold;
}
.sig-name {
font-size: 1.1em;
}
code.descname {
font-size: 1.2em;
}
.sig-prename, code.descclassname {
background-color: transparent;
}
.optional {
font-size: 1.3em;
}
.sig-paren {
font-size: larger;
}
.sig-param.n {
font-style: italic;
}
/* C++ specific styling */
.sig-inline.c-texpr,
.sig-inline.cpp-texpr {
font-family: unset;
}
.sig.c .k, .sig.c .kt,
.sig.cpp .k, .sig.cpp .kt {
color: #0033B3;
}
.sig.c .m,
.sig.cpp .m {
color: #1750EB;
}
.sig.c .s, .sig.c .sc,
.sig.cpp .s, .sig.cpp .sc {
color: #067D17;
}
/* -- other body styles ----------------------------------------------------- */
ol.arabic {
list-style: decimal;
}
ol.loweralpha {
list-style: lower-alpha;
}
ol.upperalpha {
list-style: upper-alpha;
}
ol.lowerroman {
list-style: lower-roman;
}
ol.upperroman {
list-style: upper-roman;
}
:not(li) > ol > li:first-child > :first-child,
:not(li) > ul > li:first-child > :first-child {
margin-top: 0px;
}
:not(li) > ol > li:last-child > :last-child,
:not(li) > ul > li:last-child > :last-child {
margin-bottom: 0px;
}
ol.simple ol p,
ol.simple ul p,
ul.simple ol p,
ul.simple ul p {
margin-top: 0;
}
ol.simple > li:not(:first-child) > p,
ul.simple > li:not(:first-child) > p {
margin-top: 0;
}
ol.simple p,
ul.simple p {
margin-bottom: 0;
}
aside.footnote > span,
div.citation > span {
float: left;
}
aside.footnote > span:last-of-type,
div.citation > span:last-of-type {
padding-right: 0.5em;
}
aside.footnote > p {
margin-left: 2em;
}
div.citation > p {
margin-left: 4em;
}
aside.footnote > p:last-of-type,
div.citation > p:last-of-type {
margin-bottom: 0em;
}
aside.footnote > p:last-of-type:after,
div.citation > p:last-of-type:after {
content: "";
clear: both;
}
dl.field-list {
display: grid;
grid-template-columns: fit-content(30%) auto;
}
dl.field-list > dt {
font-weight: bold;
word-break: break-word;
padding-left: 0.5em;
padding-right: 5px;
}
dl.field-list > dd {
padding-left: 0.5em;
margin-top: 0em;
margin-left: 0em;
margin-bottom: 0em;
}
dl {
margin-bottom: 15px;
}
dd > :first-child {
margin-top: 0px;
}
dd ul, dd table {
margin-bottom: 10px;
}
dd {
margin-top: 3px;
margin-bottom: 10px;
margin-left: 30px;
}
.sig dd {
margin-top: 0px;
margin-bottom: 0px;
}
.sig dl {
margin-top: 0px;
margin-bottom: 0px;
}
dl > dd:last-child,
dl > dd:last-child > :last-child {
margin-bottom: 0;
}
dt:target, span.highlighted {
background-color: #fbe54e;
}
rect.highlighted {
fill: #fbe54e;
}
dl.glossary dt {
font-weight: bold;
font-size: 1.1em;
}
.versionmodified {
font-style: italic;
}
.system-message {
background-color: #fda;
padding: 5px;
border: 3px solid red;
}
.footnote:target {
background-color: #ffa;
}
.line-block {
display: block;
margin-top: 1em;
margin-bottom: 1em;
}
.line-block .line-block {
margin-top: 0;
margin-bottom: 0;
margin-left: 1.5em;
}
.guilabel, .menuselection {
font-family: sans-serif;
}
.accelerator {
text-decoration: underline;
}
.classifier {
font-style: oblique;
}
.classifier:before {
font-style: normal;
margin: 0 0.5em;
content: ":";
display: inline-block;
}
abbr, acronym {
border-bottom: dotted 1px;
cursor: help;
}
/* -- code displays --------------------------------------------------------- */
pre {
overflow: auto;
overflow-y: hidden; /* fixes display issues on Chrome browsers */
}
pre, div[class*="highlight-"] {
clear: both;
}
span.pre {
-moz-hyphens: none;
-ms-hyphens: none;
-webkit-hyphens: none;
hyphens: none;
white-space: nowrap;
}
div[class*="highlight-"] {
margin: 1em 0;
}
td.linenos pre {
border: 0;
background-color: transparent;
color: #aaa;
}
table.highlighttable {
display: block;
}
table.highlighttable tbody {
display: block;
}
table.highlighttable tr {
display: flex;
}
table.highlighttable td {
margin: 0;
padding: 0;
}
table.highlighttable td.linenos {
padding-right: 0.5em;
}
table.highlighttable td.code {
flex: 1;
overflow: hidden;
}
.highlight .hll {
display: block;
}
div.highlight pre,
table.highlighttable pre {
margin: 0;
}
div.code-block-caption + div {
margin-top: 0;
}
div.code-block-caption {
margin-top: 1em;
padding: 2px 5px;
font-size: small;
}
div.code-block-caption code {
background-color: transparent;
}
table.highlighttable td.linenos,
span.linenos,
div.highlight span.gp { /* gp: Generic.Prompt */
user-select: none;
-webkit-user-select: text; /* Safari fallback only */
-webkit-user-select: none; /* Chrome/Safari */
-moz-user-select: none; /* Firefox */
-ms-user-select: none; /* IE10+ */
}
div.code-block-caption span.caption-number {
padding: 0.1em 0.3em;
font-style: italic;
}
div.code-block-caption span.caption-text {
}
div.literal-block-wrapper {
margin: 1em 0;
}
code.xref, a code {
background-color: transparent;
font-weight: bold;
}
h1 code, h2 code, h3 code, h4 code, h5 code, h6 code {
background-color: transparent;
}
.viewcode-link {
float: right;
}
.viewcode-back {
float: right;
font-family: sans-serif;
}
div.viewcode-block:target {
margin: -1px -10px;
padding: 0 10px;
}
/* -- math display ---------------------------------------------------------- */
img.math {
vertical-align: middle;
}
div.body div.math p {
text-align: center;
}
span.eqno {
float: right;
}
span.eqno a.headerlink {
position: absolute;
z-index: 1;
}
div.math:hover a.headerlink {
visibility: visible;
}
/* -- printout stylesheet --------------------------------------------------- */
@media print {
div.document,
div.documentwrapper,
div.bodywrapper {
margin: 0 !important;
width: 100%;
}
div.sphinxsidebar,
div.related,
div.footer,
#top-link {
display: none;
}
}
+1
View File
@@ -0,0 +1 @@
.clearfix{*zoom:1}.clearfix:after,.clearfix:before{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-style:normal;font-weight:400;src:url(fonts/fontawesome-webfont.eot?674f50d287a8c48dc19ba404d20fe713?#iefix) format("embedded-opentype"),url(fonts/fontawesome-webfont.woff2?af7ae505a9eed503f8b8e6982036873e) format("woff2"),url(fonts/fontawesome-webfont.woff?fee66e712a8a08eef5805a46892932ad) format("woff"),url(fonts/fontawesome-webfont.ttf?b06871f281fee6b241d60582ae9369b9) format("truetype"),url(fonts/fontawesome-webfont.svg?912ec66d7572ff821749319396470bde#FontAwesome) format("svg")}.fa:before{font-family:FontAwesome;font-style:normal;font-weight:400;line-height:1}.fa:before,a .fa{text-decoration:inherit}.fa:before,a .fa,li .fa{display:inline-block}li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-.8em}ul.fas li .fa{width:.8em}ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before,.icon-book:before{content:"\f02d"}.fa-caret-down:before,.icon-caret-down:before{content:"\f0d7"}.fa-caret-up:before,.icon-caret-up:before{content:"\f0d8"}.fa-caret-left:before,.icon-caret-left:before{content:"\f0d9"}.fa-caret-right:before,.icon-caret-right:before{content:"\f0da"}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;font-family:Lato,proxima-nova,Helvetica Neue,Arial,sans-serif;z-index:400}.rst-versions a{color:#2980b9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27ae60}.rst-versions .rst-current-version:after{clear:both;content:"";display:block}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book,.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#e74c3c;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#f1c40f;color:#000}.rst-versions.shift-up{height:auto;max-height:100%;overflow-y:scroll}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:grey;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:1px solid #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions .rst-other-versions .rtd-current-item{font-weight:700}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px;max-height:90%}.rst-versions.rst-badge .fa-book,.rst-versions.rst-badge .icon-book{float:none;line-height:30px}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book,.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge>.rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width:768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}}#flyout-search-form{padding:6px}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 434 KiB

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
+149
View File
@@ -0,0 +1,149 @@
/*
* Base JavaScript utilities for all Sphinx HTML documentation.
*/
"use strict";
const BLACKLISTED_KEY_CONTROL_ELEMENTS = new Set([
"TEXTAREA",
"INPUT",
"SELECT",
"BUTTON",
]);
const _ready = (callback) => {
if (document.readyState !== "loading") {
callback();
} else {
document.addEventListener("DOMContentLoaded", callback);
}
};
/**
* Small JavaScript module for the documentation.
*/
const Documentation = {
init: () => {
Documentation.initDomainIndexTable();
Documentation.initOnKeyListeners();
},
/**
* i18n support
*/
TRANSLATIONS: {},
PLURAL_EXPR: (n) => (n === 1 ? 0 : 1),
LOCALE: "unknown",
// gettext and ngettext don't access this so that the functions
// can safely bound to a different name (_ = Documentation.gettext)
gettext: (string) => {
const translated = Documentation.TRANSLATIONS[string];
switch (typeof translated) {
case "undefined":
return string; // no translation
case "string":
return translated; // translation exists
default:
return translated[0]; // (singular, plural) translation tuple exists
}
},
ngettext: (singular, plural, n) => {
const translated = Documentation.TRANSLATIONS[singular];
if (typeof translated !== "undefined")
return translated[Documentation.PLURAL_EXPR(n)];
return n === 1 ? singular : plural;
},
addTranslations: (catalog) => {
Object.assign(Documentation.TRANSLATIONS, catalog.messages);
Documentation.PLURAL_EXPR = new Function(
"n",
`return (${catalog.plural_expr})`
);
Documentation.LOCALE = catalog.locale;
},
/**
* helper function to focus on search bar
*/
focusSearchBar: () => {
document.querySelectorAll("input[name=q]")[0]?.focus();
},
/**
* Initialise the domain index toggle buttons
*/
initDomainIndexTable: () => {
const toggler = (el) => {
const idNumber = el.id.substr(7);
const toggledRows = document.querySelectorAll(`tr.cg-${idNumber}`);
if (el.src.substr(-9) === "minus.png") {
el.src = `${el.src.substr(0, el.src.length - 9)}plus.png`;
toggledRows.forEach((el) => (el.style.display = "none"));
} else {
el.src = `${el.src.substr(0, el.src.length - 8)}minus.png`;
toggledRows.forEach((el) => (el.style.display = ""));
}
};
const togglerElements = document.querySelectorAll("img.toggler");
togglerElements.forEach((el) =>
el.addEventListener("click", (event) => toggler(event.currentTarget))
);
togglerElements.forEach((el) => (el.style.display = ""));
if (DOCUMENTATION_OPTIONS.COLLAPSE_INDEX) togglerElements.forEach(toggler);
},
initOnKeyListeners: () => {
// only install a listener if it is really needed
if (
!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS &&
!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS
)
return;
document.addEventListener("keydown", (event) => {
// bail for input elements
if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return;
// bail with special keys
if (event.altKey || event.ctrlKey || event.metaKey) return;
if (!event.shiftKey) {
switch (event.key) {
case "ArrowLeft":
if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break;
const prevLink = document.querySelector('link[rel="prev"]');
if (prevLink && prevLink.href) {
window.location.href = prevLink.href;
event.preventDefault();
}
break;
case "ArrowRight":
if (!DOCUMENTATION_OPTIONS.NAVIGATION_WITH_KEYS) break;
const nextLink = document.querySelector('link[rel="next"]');
if (nextLink && nextLink.href) {
window.location.href = nextLink.href;
event.preventDefault();
}
break;
}
}
// some keyboard layouts may need Shift to get /
switch (event.key) {
case "/":
if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) break;
Documentation.focusSearchBar();
event.preventDefault();
}
});
},
};
// quick alias for translations
const _ = Documentation.gettext;
_ready(Documentation.init);
+13
View File
@@ -0,0 +1,13 @@
const DOCUMENTATION_OPTIONS = {
VERSION: '1.1.1',
LANGUAGE: 'en',
COLLAPSE_INDEX: false,
BUILDER: 'html',
FILE_SUFFIX: '.html',
LINK_SUFFIX: '.html',
HAS_SOURCE: true,
SOURCELINK_SUFFIX: '.txt',
NAVIGATION_WITH_KEYS: false,
SHOW_SEARCH_SUMMARY: true,
ENABLE_SEARCH_SHORTCUTS: true,
};
Binary file not shown.

After

Width:  |  Height:  |  Size: 286 B

Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
+1
View File
@@ -0,0 +1 @@
!function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=4)}({4:function(e,t,r){}});
File diff suppressed because one or more lines are too long
+228
View File
@@ -0,0 +1,228 @@
const themeFlyoutDisplay = "hidden";
const themeVersionSelector = true;
const themeLanguageSelector = true;
if (themeFlyoutDisplay === "attached") {
function renderLanguages(config) {
if (!config.projects.translations.length) {
return "";
}
// Insert the current language to the options on the selector
let languages = config.projects.translations.concat(config.projects.current);
languages = languages.sort((a, b) => a.language.name.localeCompare(b.language.name));
const languagesHTML = `
<dl>
<dt>Languages</dt>
${languages
.map(
(translation) => `
<dd ${translation.slug == config.projects.current.slug ? 'class="rtd-current-item"' : ""}>
<a href="${translation.urls.documentation}">${translation.language.code}</a>
</dd>
`,
)
.join("\n")}
</dl>
`;
return languagesHTML;
}
function renderVersions(config) {
if (!config.versions.active.length) {
return "";
}
const versionsHTML = `
<dl>
<dt>Versions</dt>
${config.versions.active
.map(
(version) => `
<dd ${version.slug === config.versions.current.slug ? 'class="rtd-current-item"' : ""}>
<a href="${version.urls.documentation}">${version.slug}</a>
</dd>
`,
)
.join("\n")}
</dl>
`;
return versionsHTML;
}
function renderDownloads(config) {
if (!Object.keys(config.versions.current.downloads).length) {
return "";
}
const downloadsNameDisplay = {
pdf: "PDF",
epub: "Epub",
htmlzip: "HTML",
};
const downloadsHTML = `
<dl>
<dt>Downloads</dt>
${Object.entries(config.versions.current.downloads)
.map(
([name, url]) => `
<dd>
<a href="${url}">${downloadsNameDisplay[name]}</a>
</dd>
`,
)
.join("\n")}
</dl>
`;
return downloadsHTML;
}
document.addEventListener("readthedocs-addons-data-ready", function (event) {
const config = event.detail.data();
const flyout = `
<div class="rst-versions" data-toggle="rst-versions" role="note">
<span class="rst-current-version" data-toggle="rst-current-version">
<span class="fa fa-book"> Read the Docs</span>
v: ${config.versions.current.slug}
<span class="fa fa-caret-down"></span>
</span>
<div class="rst-other-versions">
<div class="injected">
${renderLanguages(config)}
${renderVersions(config)}
${renderDownloads(config)}
<dl>
<dt>On Read the Docs</dt>
<dd>
<a href="${config.projects.current.urls.home}">Project Home</a>
</dd>
<dd>
<a href="${config.projects.current.urls.builds}">Builds</a>
</dd>
<dd>
<a href="${config.projects.current.urls.downloads}">Downloads</a>
</dd>
</dl>
<dl>
<dt>Search</dt>
<dd>
<form id="flyout-search-form">
<input
class="wy-form"
type="text"
name="q"
aria-label="Search docs"
placeholder="Search docs"
/>
</form>
</dd>
</dl>
<hr />
<small>
<span>Hosted by <a href="https://about.readthedocs.org/?utm_source=&utm_content=flyout">Read the Docs</a></span>
</small>
</div>
</div>
`;
// Inject the generated flyout into the body HTML element.
document.body.insertAdjacentHTML("beforeend", flyout);
// Trigger the Read the Docs Addons Search modal when clicking on the "Search docs" input from inside the flyout.
document
.querySelector("#flyout-search-form")
.addEventListener("focusin", () => {
const event = new CustomEvent("readthedocs-search-show");
document.dispatchEvent(event);
});
})
}
if (themeLanguageSelector || themeVersionSelector) {
function onSelectorSwitch(event) {
const option = event.target.selectedIndex;
const item = event.target.options[option];
window.location.href = item.dataset.url;
}
document.addEventListener("readthedocs-addons-data-ready", function (event) {
const config = event.detail.data();
const versionSwitch = document.querySelector(
"div.switch-menus > div.version-switch",
);
if (themeVersionSelector) {
let versions = config.versions.active;
if (config.versions.current.hidden || config.versions.current.type === "external") {
versions.unshift(config.versions.current);
}
const versionSelect = `
<select>
${versions
.map(
(version) => `
<option
value="${version.slug}"
${config.versions.current.slug === version.slug ? 'selected="selected"' : ""}
data-url="${version.urls.documentation}">
${version.slug}
</option>`,
)
.join("\n")}
</select>
`;
versionSwitch.innerHTML = versionSelect;
versionSwitch.firstElementChild.addEventListener("change", onSelectorSwitch);
}
const languageSwitch = document.querySelector(
"div.switch-menus > div.language-switch",
);
if (themeLanguageSelector) {
if (config.projects.translations.length) {
// Add the current language to the options on the selector
let languages = config.projects.translations.concat(
config.projects.current,
);
languages = languages.sort((a, b) =>
a.language.name.localeCompare(b.language.name),
);
const languageSelect = `
<select>
${languages
.map(
(language) => `
<option
value="${language.language.code}"
${config.projects.current.slug === language.slug ? 'selected="selected"' : ""}
data-url="${language.urls.documentation}">
${language.language.name}
</option>`,
)
.join("\n")}
</select>
`;
languageSwitch.innerHTML = languageSelect;
languageSwitch.firstElementChild.addEventListener("change", onSelectorSwitch);
}
else {
languageSwitch.remove();
}
}
});
}
document.addEventListener("readthedocs-addons-data-ready", function (event) {
// Trigger the Read the Docs Addons Search modal when clicking on "Search docs" input from the topnav.
document
.querySelector("[role='search'] input")
.addEventListener("focusin", () => {
const event = new CustomEvent("readthedocs-search-show");
document.dispatchEvent(event);
});
});
+192
View File
@@ -0,0 +1,192 @@
/*
* This script contains the language-specific data used by searchtools.js,
* namely the list of stopwords, stemmer, scorer and splitter.
*/
var stopwords = ["a", "and", "are", "as", "at", "be", "but", "by", "for", "if", "in", "into", "is", "it", "near", "no", "not", "of", "on", "or", "such", "that", "the", "their", "then", "there", "these", "they", "this", "to", "was", "will", "with"];
/* Non-minified version is copied as a separate JS file, if available */
/**
* Porter Stemmer
*/
var Stemmer = function() {
var step2list = {
ational: 'ate',
tional: 'tion',
enci: 'ence',
anci: 'ance',
izer: 'ize',
bli: 'ble',
alli: 'al',
entli: 'ent',
eli: 'e',
ousli: 'ous',
ization: 'ize',
ation: 'ate',
ator: 'ate',
alism: 'al',
iveness: 'ive',
fulness: 'ful',
ousness: 'ous',
aliti: 'al',
iviti: 'ive',
biliti: 'ble',
logi: 'log'
};
var step3list = {
icate: 'ic',
ative: '',
alize: 'al',
iciti: 'ic',
ical: 'ic',
ful: '',
ness: ''
};
var c = "[^aeiou]"; // consonant
var v = "[aeiouy]"; // vowel
var C = c + "[^aeiouy]*"; // consonant sequence
var V = v + "[aeiou]*"; // vowel sequence
var mgr0 = "^(" + C + ")?" + V + C; // [C]VC... is m>0
var meq1 = "^(" + C + ")?" + V + C + "(" + V + ")?$"; // [C]VC[V] is m=1
var mgr1 = "^(" + C + ")?" + V + C + V + C; // [C]VCVC... is m>1
var s_v = "^(" + C + ")?" + v; // vowel in stem
this.stemWord = function (w) {
var stem;
var suffix;
var firstch;
var origword = w;
if (w.length < 3)
return w;
var re;
var re2;
var re3;
var re4;
firstch = w.substr(0,1);
if (firstch == "y")
w = firstch.toUpperCase() + w.substr(1);
// Step 1a
re = /^(.+?)(ss|i)es$/;
re2 = /^(.+?)([^s])s$/;
if (re.test(w))
w = w.replace(re,"$1$2");
else if (re2.test(w))
w = w.replace(re2,"$1$2");
// Step 1b
re = /^(.+?)eed$/;
re2 = /^(.+?)(ed|ing)$/;
if (re.test(w)) {
var fp = re.exec(w);
re = new RegExp(mgr0);
if (re.test(fp[1])) {
re = /.$/;
w = w.replace(re,"");
}
}
else if (re2.test(w)) {
var fp = re2.exec(w);
stem = fp[1];
re2 = new RegExp(s_v);
if (re2.test(stem)) {
w = stem;
re2 = /(at|bl|iz)$/;
re3 = new RegExp("([^aeiouylsz])\\1$");
re4 = new RegExp("^" + C + v + "[^aeiouwxy]$");
if (re2.test(w))
w = w + "e";
else if (re3.test(w)) {
re = /.$/;
w = w.replace(re,"");
}
else if (re4.test(w))
w = w + "e";
}
}
// Step 1c
re = /^(.+?)y$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
re = new RegExp(s_v);
if (re.test(stem))
w = stem + "i";
}
// Step 2
re = /^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
suffix = fp[2];
re = new RegExp(mgr0);
if (re.test(stem))
w = stem + step2list[suffix];
}
// Step 3
re = /^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
suffix = fp[2];
re = new RegExp(mgr0);
if (re.test(stem))
w = stem + step3list[suffix];
}
// Step 4
re = /^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/;
re2 = /^(.+?)(s|t)(ion)$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
re = new RegExp(mgr1);
if (re.test(stem))
w = stem;
}
else if (re2.test(w)) {
var fp = re2.exec(w);
stem = fp[1] + fp[2];
re2 = new RegExp(mgr1);
if (re2.test(stem))
w = stem;
}
// Step 5
re = /^(.+?)e$/;
if (re.test(w)) {
var fp = re.exec(w);
stem = fp[1];
re = new RegExp(mgr1);
re2 = new RegExp(meq1);
re3 = new RegExp("^" + C + v + "[^aeiouwxy]$");
if (re.test(stem) || (re2.test(stem) && !(re3.test(stem))))
w = stem;
}
re = /ll$/;
re2 = new RegExp(mgr1);
if (re.test(w) && re2.test(w)) {
re = /.$/;
w = w.replace(re,"");
}
// and turn initial Y back to y
if (firstch == "y")
w = firstch.toLowerCase() + w.substr(1);
return w;
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 90 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 B

+75
View File
@@ -0,0 +1,75 @@
pre { line-height: 125%; }
td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; }
td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; }
.highlight .hll { background-color: #ffffcc }
.highlight { background: #f8f8f8; }
.highlight .c { color: #3D7B7B; font-style: italic } /* Comment */
.highlight .err { border: 1px solid #F00 } /* Error */
.highlight .k { color: #008000; font-weight: bold } /* Keyword */
.highlight .o { color: #666 } /* Operator */
.highlight .ch { color: #3D7B7B; font-style: italic } /* Comment.Hashbang */
.highlight .cm { color: #3D7B7B; font-style: italic } /* Comment.Multiline */
.highlight .cp { color: #9C6500 } /* Comment.Preproc */
.highlight .cpf { color: #3D7B7B; font-style: italic } /* Comment.PreprocFile */
.highlight .c1 { color: #3D7B7B; font-style: italic } /* Comment.Single */
.highlight .cs { color: #3D7B7B; font-style: italic } /* Comment.Special */
.highlight .gd { color: #A00000 } /* Generic.Deleted */
.highlight .ge { font-style: italic } /* Generic.Emph */
.highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */
.highlight .gr { color: #E40000 } /* Generic.Error */
.highlight .gh { color: #000080; font-weight: bold } /* Generic.Heading */
.highlight .gi { color: #008400 } /* Generic.Inserted */
.highlight .go { color: #717171 } /* Generic.Output */
.highlight .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
.highlight .gs { font-weight: bold } /* Generic.Strong */
.highlight .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
.highlight .gt { color: #04D } /* Generic.Traceback */
.highlight .kc { color: #008000; font-weight: bold } /* Keyword.Constant */
.highlight .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */
.highlight .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */
.highlight .kp { color: #008000 } /* Keyword.Pseudo */
.highlight .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */
.highlight .kt { color: #B00040 } /* Keyword.Type */
.highlight .m { color: #666 } /* Literal.Number */
.highlight .s { color: #BA2121 } /* Literal.String */
.highlight .na { color: #687822 } /* Name.Attribute */
.highlight .nb { color: #008000 } /* Name.Builtin */
.highlight .nc { color: #00F; font-weight: bold } /* Name.Class */
.highlight .no { color: #800 } /* Name.Constant */
.highlight .nd { color: #A2F } /* Name.Decorator */
.highlight .ni { color: #717171; font-weight: bold } /* Name.Entity */
.highlight .ne { color: #CB3F38; font-weight: bold } /* Name.Exception */
.highlight .nf { color: #00F } /* Name.Function */
.highlight .nl { color: #767600 } /* Name.Label */
.highlight .nn { color: #00F; font-weight: bold } /* Name.Namespace */
.highlight .nt { color: #008000; font-weight: bold } /* Name.Tag */
.highlight .nv { color: #19177C } /* Name.Variable */
.highlight .ow { color: #A2F; font-weight: bold } /* Operator.Word */
.highlight .w { color: #BBB } /* Text.Whitespace */
.highlight .mb { color: #666 } /* Literal.Number.Bin */
.highlight .mf { color: #666 } /* Literal.Number.Float */
.highlight .mh { color: #666 } /* Literal.Number.Hex */
.highlight .mi { color: #666 } /* Literal.Number.Integer */
.highlight .mo { color: #666 } /* Literal.Number.Oct */
.highlight .sa { color: #BA2121 } /* Literal.String.Affix */
.highlight .sb { color: #BA2121 } /* Literal.String.Backtick */
.highlight .sc { color: #BA2121 } /* Literal.String.Char */
.highlight .dl { color: #BA2121 } /* Literal.String.Delimiter */
.highlight .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */
.highlight .s2 { color: #BA2121 } /* Literal.String.Double */
.highlight .se { color: #AA5D1F; font-weight: bold } /* Literal.String.Escape */
.highlight .sh { color: #BA2121 } /* Literal.String.Heredoc */
.highlight .si { color: #A45A77; font-weight: bold } /* Literal.String.Interpol */
.highlight .sx { color: #008000 } /* Literal.String.Other */
.highlight .sr { color: #A45A77 } /* Literal.String.Regex */
.highlight .s1 { color: #BA2121 } /* Literal.String.Single */
.highlight .ss { color: #19177C } /* Literal.String.Symbol */
.highlight .bp { color: #008000 } /* Name.Builtin.Pseudo */
.highlight .fm { color: #00F } /* Name.Function.Magic */
.highlight .vc { color: #19177C } /* Name.Variable.Class */
.highlight .vg { color: #19177C } /* Name.Variable.Global */
.highlight .vi { color: #19177C } /* Name.Variable.Instance */
.highlight .vm { color: #19177C } /* Name.Variable.Magic */
.highlight .il { color: #666 } /* Literal.Number.Integer.Long */
+635
View File
@@ -0,0 +1,635 @@
/*
* Sphinx JavaScript utilities for the full-text search.
*/
"use strict";
/**
* Simple result scoring code.
*/
if (typeof Scorer === "undefined") {
var Scorer = {
// Implement the following function to further tweak the score for each result
// The function takes a result array [docname, title, anchor, descr, score, filename]
// and returns the new score.
/*
score: result => {
const [docname, title, anchor, descr, score, filename, kind] = result
return score
},
*/
// query matches the full name of an object
objNameMatch: 11,
// or matches in the last dotted part of the object name
objPartialMatch: 6,
// Additive scores depending on the priority of the object
objPrio: {
0: 15, // used to be importantResults
1: 5, // used to be objectResults
2: -5, // used to be unimportantResults
},
// Used when the priority is not in the mapping.
objPrioDefault: 0,
// query found in title
title: 15,
partialTitle: 7,
// query found in terms
term: 5,
partialTerm: 2,
};
}
// Global search result kind enum, used by themes to style search results.
class SearchResultKind {
static get index() { return "index"; }
static get object() { return "object"; }
static get text() { return "text"; }
static get title() { return "title"; }
}
const _removeChildren = (element) => {
while (element && element.lastChild) element.removeChild(element.lastChild);
};
/**
* See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#escaping
*/
const _escapeRegExp = (string) =>
string.replace(/[.*+\-?^${}()|[\]\\]/g, "\\$&"); // $& means the whole matched string
const _displayItem = (item, searchTerms, highlightTerms) => {
const docBuilder = DOCUMENTATION_OPTIONS.BUILDER;
const docFileSuffix = DOCUMENTATION_OPTIONS.FILE_SUFFIX;
const docLinkSuffix = DOCUMENTATION_OPTIONS.LINK_SUFFIX;
const showSearchSummary = DOCUMENTATION_OPTIONS.SHOW_SEARCH_SUMMARY;
const contentRoot = document.documentElement.dataset.content_root;
const [docName, title, anchor, descr, score, _filename, kind] = item;
let listItem = document.createElement("li");
// Add a class representing the item's type:
// can be used by a theme's CSS selector for styling
// See SearchResultKind for the class names.
listItem.classList.add(`kind-${kind}`);
let requestUrl;
let linkUrl;
if (docBuilder === "dirhtml") {
// dirhtml builder
let dirname = docName + "/";
if (dirname.match(/\/index\/$/))
dirname = dirname.substring(0, dirname.length - 6);
else if (dirname === "index/") dirname = "";
requestUrl = contentRoot + dirname;
linkUrl = requestUrl;
} else {
// normal html builders
requestUrl = contentRoot + docName + docFileSuffix;
linkUrl = docName + docLinkSuffix;
}
let linkEl = listItem.appendChild(document.createElement("a"));
linkEl.href = linkUrl + anchor;
linkEl.dataset.score = score;
linkEl.innerHTML = title;
if (descr) {
listItem.appendChild(document.createElement("span")).innerHTML =
" (" + descr + ")";
// highlight search terms in the description
if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js
highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted"));
}
else if (showSearchSummary)
fetch(requestUrl)
.then((responseData) => responseData.text())
.then((data) => {
if (data)
listItem.appendChild(
Search.makeSearchSummary(data, searchTerms, anchor)
);
// highlight search terms in the summary
if (SPHINX_HIGHLIGHT_ENABLED) // set in sphinx_highlight.js
highlightTerms.forEach((term) => _highlightText(listItem, term, "highlighted"));
});
Search.output.appendChild(listItem);
};
const _finishSearch = (resultCount) => {
Search.stopPulse();
Search.title.innerText = _("Search Results");
if (!resultCount)
Search.status.innerText = Documentation.gettext(
"Your search did not match any documents. Please make sure that all words are spelled correctly and that you've selected enough categories."
);
else
Search.status.innerText = Documentation.ngettext(
"Search finished, found one page matching the search query.",
"Search finished, found ${resultCount} pages matching the search query.",
resultCount,
).replace('${resultCount}', resultCount);
};
const _displayNextItem = (
results,
resultCount,
searchTerms,
highlightTerms,
) => {
// results left, load the summary and display it
// this is intended to be dynamic (don't sub resultsCount)
if (results.length) {
_displayItem(results.pop(), searchTerms, highlightTerms);
setTimeout(
() => _displayNextItem(results, resultCount, searchTerms, highlightTerms),
5
);
}
// search finished, update title and status message
else _finishSearch(resultCount);
};
// Helper function used by query() to order search results.
// Each input is an array of [docname, title, anchor, descr, score, filename, kind].
// Order the results by score (in opposite order of appearance, since the
// `_displayNextItem` function uses pop() to retrieve items) and then alphabetically.
const _orderResultsByScoreThenName = (a, b) => {
const leftScore = a[4];
const rightScore = b[4];
if (leftScore === rightScore) {
// same score: sort alphabetically
const leftTitle = a[1].toLowerCase();
const rightTitle = b[1].toLowerCase();
if (leftTitle === rightTitle) return 0;
return leftTitle > rightTitle ? -1 : 1; // inverted is intentional
}
return leftScore > rightScore ? 1 : -1;
};
/**
* Default splitQuery function. Can be overridden in ``sphinx.search`` with a
* custom function per language.
*
* The regular expression works by splitting the string on consecutive characters
* that are not Unicode letters, numbers, underscores, or emoji characters.
* This is the same as ``\W+`` in Python, preserving the surrogate pair area.
*/
if (typeof splitQuery === "undefined") {
var splitQuery = (query) => query
.split(/[^\p{Letter}\p{Number}_\p{Emoji_Presentation}]+/gu)
.filter(term => term) // remove remaining empty strings
}
/**
* Search Module
*/
const Search = {
_index: null,
_queued_query: null,
_pulse_status: -1,
htmlToText: (htmlString, anchor) => {
const htmlElement = new DOMParser().parseFromString(htmlString, 'text/html');
for (const removalQuery of [".headerlink", "script", "style"]) {
htmlElement.querySelectorAll(removalQuery).forEach((el) => { el.remove() });
}
if (anchor) {
const anchorContent = htmlElement.querySelector(`[role="main"] ${anchor}`);
if (anchorContent) return anchorContent.textContent;
console.warn(
`Anchored content block not found. Sphinx search tries to obtain it via DOM query '[role=main] ${anchor}'. Check your theme or template.`
);
}
// if anchor not specified or not found, fall back to main content
const docContent = htmlElement.querySelector('[role="main"]');
if (docContent) return docContent.textContent;
console.warn(
"Content block not found. Sphinx search tries to obtain it via DOM query '[role=main]'. Check your theme or template."
);
return "";
},
init: () => {
const query = new URLSearchParams(window.location.search).get("q");
document
.querySelectorAll('input[name="q"]')
.forEach((el) => (el.value = query));
if (query) Search.performSearch(query);
},
loadIndex: (url) =>
(document.body.appendChild(document.createElement("script")).src = url),
setIndex: (index) => {
Search._index = index;
if (Search._queued_query !== null) {
const query = Search._queued_query;
Search._queued_query = null;
Search.query(query);
}
},
hasIndex: () => Search._index !== null,
deferQuery: (query) => (Search._queued_query = query),
stopPulse: () => (Search._pulse_status = -1),
startPulse: () => {
if (Search._pulse_status >= 0) return;
const pulse = () => {
Search._pulse_status = (Search._pulse_status + 1) % 4;
Search.dots.innerText = ".".repeat(Search._pulse_status);
if (Search._pulse_status >= 0) window.setTimeout(pulse, 500);
};
pulse();
},
/**
* perform a search for something (or wait until index is loaded)
*/
performSearch: (query) => {
// create the required interface elements
const searchText = document.createElement("h2");
searchText.textContent = _("Searching");
const searchSummary = document.createElement("p");
searchSummary.classList.add("search-summary");
searchSummary.innerText = "";
const searchList = document.createElement("ul");
searchList.setAttribute("role", "list");
searchList.classList.add("search");
const out = document.getElementById("search-results");
Search.title = out.appendChild(searchText);
Search.dots = Search.title.appendChild(document.createElement("span"));
Search.status = out.appendChild(searchSummary);
Search.output = out.appendChild(searchList);
const searchProgress = document.getElementById("search-progress");
// Some themes don't use the search progress node
if (searchProgress) {
searchProgress.innerText = _("Preparing search...");
}
Search.startPulse();
// index already loaded, the browser was quick!
if (Search.hasIndex()) Search.query(query);
else Search.deferQuery(query);
},
_parseQuery: (query) => {
// stem the search terms and add them to the correct list
const stemmer = new Stemmer();
const searchTerms = new Set();
const excludedTerms = new Set();
const highlightTerms = new Set();
const objectTerms = new Set(splitQuery(query.toLowerCase().trim()));
splitQuery(query.trim()).forEach((queryTerm) => {
const queryTermLower = queryTerm.toLowerCase();
// maybe skip this "word"
// stopwords array is from language_data.js
if (
stopwords.indexOf(queryTermLower) !== -1 ||
queryTerm.match(/^\d+$/)
)
return;
// stem the word
let word = stemmer.stemWord(queryTermLower);
// select the correct list
if (word[0] === "-") excludedTerms.add(word.substr(1));
else {
searchTerms.add(word);
highlightTerms.add(queryTermLower);
}
});
if (SPHINX_HIGHLIGHT_ENABLED) { // set in sphinx_highlight.js
localStorage.setItem("sphinx_highlight_terms", [...highlightTerms].join(" "))
}
// console.debug("SEARCH: searching for:");
// console.info("required: ", [...searchTerms]);
// console.info("excluded: ", [...excludedTerms]);
return [query, searchTerms, excludedTerms, highlightTerms, objectTerms];
},
/**
* execute search (requires search index to be loaded)
*/
_performSearch: (query, searchTerms, excludedTerms, highlightTerms, objectTerms) => {
const filenames = Search._index.filenames;
const docNames = Search._index.docnames;
const titles = Search._index.titles;
const allTitles = Search._index.alltitles;
const indexEntries = Search._index.indexentries;
// Collect multiple result groups to be sorted separately and then ordered.
// Each is an array of [docname, title, anchor, descr, score, filename, kind].
const normalResults = [];
const nonMainIndexResults = [];
_removeChildren(document.getElementById("search-progress"));
const queryLower = query.toLowerCase().trim();
for (const [title, foundTitles] of Object.entries(allTitles)) {
if (title.toLowerCase().trim().includes(queryLower) && (queryLower.length >= title.length/2)) {
for (const [file, id] of foundTitles) {
const score = Math.round(Scorer.title * queryLower.length / title.length);
const boost = titles[file] === title ? 1 : 0; // add a boost for document titles
normalResults.push([
docNames[file],
titles[file] !== title ? `${titles[file]} > ${title}` : title,
id !== null ? "#" + id : "",
null,
score + boost,
filenames[file],
SearchResultKind.title,
]);
}
}
}
// search for explicit entries in index directives
for (const [entry, foundEntries] of Object.entries(indexEntries)) {
if (entry.includes(queryLower) && (queryLower.length >= entry.length/2)) {
for (const [file, id, isMain] of foundEntries) {
const score = Math.round(100 * queryLower.length / entry.length);
const result = [
docNames[file],
titles[file],
id ? "#" + id : "",
null,
score,
filenames[file],
SearchResultKind.index,
];
if (isMain) {
normalResults.push(result);
} else {
nonMainIndexResults.push(result);
}
}
}
}
// lookup as object
objectTerms.forEach((term) =>
normalResults.push(...Search.performObjectSearch(term, objectTerms))
);
// lookup as search terms in fulltext
normalResults.push(...Search.performTermsSearch(searchTerms, excludedTerms));
// let the scorer override scores with a custom scoring function
if (Scorer.score) {
normalResults.forEach((item) => (item[4] = Scorer.score(item)));
nonMainIndexResults.forEach((item) => (item[4] = Scorer.score(item)));
}
// Sort each group of results by score and then alphabetically by name.
normalResults.sort(_orderResultsByScoreThenName);
nonMainIndexResults.sort(_orderResultsByScoreThenName);
// Combine the result groups in (reverse) order.
// Non-main index entries are typically arbitrary cross-references,
// so display them after other results.
let results = [...nonMainIndexResults, ...normalResults];
// remove duplicate search results
// note the reversing of results, so that in the case of duplicates, the highest-scoring entry is kept
let seen = new Set();
results = results.reverse().reduce((acc, result) => {
let resultStr = result.slice(0, 4).concat([result[5]]).map(v => String(v)).join(',');
if (!seen.has(resultStr)) {
acc.push(result);
seen.add(resultStr);
}
return acc;
}, []);
return results.reverse();
},
query: (query) => {
const [searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms] = Search._parseQuery(query);
const results = Search._performSearch(searchQuery, searchTerms, excludedTerms, highlightTerms, objectTerms);
// for debugging
//Search.lastresults = results.slice(); // a copy
// console.info("search results:", Search.lastresults);
// print the results
_displayNextItem(results, results.length, searchTerms, highlightTerms);
},
/**
* search for object names
*/
performObjectSearch: (object, objectTerms) => {
const filenames = Search._index.filenames;
const docNames = Search._index.docnames;
const objects = Search._index.objects;
const objNames = Search._index.objnames;
const titles = Search._index.titles;
const results = [];
const objectSearchCallback = (prefix, match) => {
const name = match[4]
const fullname = (prefix ? prefix + "." : "") + name;
const fullnameLower = fullname.toLowerCase();
if (fullnameLower.indexOf(object) < 0) return;
let score = 0;
const parts = fullnameLower.split(".");
// check for different match types: exact matches of full name or
// "last name" (i.e. last dotted part)
if (fullnameLower === object || parts.slice(-1)[0] === object)
score += Scorer.objNameMatch;
else if (parts.slice(-1)[0].indexOf(object) > -1)
score += Scorer.objPartialMatch; // matches in last name
const objName = objNames[match[1]][2];
const title = titles[match[0]];
// If more than one term searched for, we require other words to be
// found in the name/title/description
const otherTerms = new Set(objectTerms);
otherTerms.delete(object);
if (otherTerms.size > 0) {
const haystack = `${prefix} ${name} ${objName} ${title}`.toLowerCase();
if (
[...otherTerms].some((otherTerm) => haystack.indexOf(otherTerm) < 0)
)
return;
}
let anchor = match[3];
if (anchor === "") anchor = fullname;
else if (anchor === "-") anchor = objNames[match[1]][1] + "-" + fullname;
const descr = objName + _(", in ") + title;
// add custom score for some objects according to scorer
if (Scorer.objPrio.hasOwnProperty(match[2]))
score += Scorer.objPrio[match[2]];
else score += Scorer.objPrioDefault;
results.push([
docNames[match[0]],
fullname,
"#" + anchor,
descr,
score,
filenames[match[0]],
SearchResultKind.object,
]);
};
Object.keys(objects).forEach((prefix) =>
objects[prefix].forEach((array) =>
objectSearchCallback(prefix, array)
)
);
return results;
},
/**
* search for full-text terms in the index
*/
performTermsSearch: (searchTerms, excludedTerms) => {
// prepare search
const terms = Search._index.terms;
const titleTerms = Search._index.titleterms;
const filenames = Search._index.filenames;
const docNames = Search._index.docnames;
const titles = Search._index.titles;
const scoreMap = new Map();
const fileMap = new Map();
// perform the search on the required terms
searchTerms.forEach((word) => {
const files = [];
// find documents, if any, containing the query word in their text/title term indices
// use Object.hasOwnProperty to avoid mismatching against prototype properties
const arr = [
{ files: terms.hasOwnProperty(word) ? terms[word] : undefined, score: Scorer.term },
{ files: titleTerms.hasOwnProperty(word) ? titleTerms[word] : undefined, score: Scorer.title },
];
// add support for partial matches
if (word.length > 2) {
const escapedWord = _escapeRegExp(word);
if (!terms.hasOwnProperty(word)) {
Object.keys(terms).forEach((term) => {
if (term.match(escapedWord))
arr.push({ files: terms[term], score: Scorer.partialTerm });
});
}
if (!titleTerms.hasOwnProperty(word)) {
Object.keys(titleTerms).forEach((term) => {
if (term.match(escapedWord))
arr.push({ files: titleTerms[term], score: Scorer.partialTitle });
});
}
}
// no match but word was a required one
if (arr.every((record) => record.files === undefined)) return;
// found search word in contents
arr.forEach((record) => {
if (record.files === undefined) return;
let recordFiles = record.files;
if (recordFiles.length === undefined) recordFiles = [recordFiles];
files.push(...recordFiles);
// set score for the word in each file
recordFiles.forEach((file) => {
if (!scoreMap.has(file)) scoreMap.set(file, new Map());
const fileScores = scoreMap.get(file);
fileScores.set(word, record.score);
});
});
// create the mapping
files.forEach((file) => {
if (!fileMap.has(file)) fileMap.set(file, [word]);
else if (fileMap.get(file).indexOf(word) === -1) fileMap.get(file).push(word);
});
});
// now check if the files don't contain excluded terms
const results = [];
for (const [file, wordList] of fileMap) {
// check if all requirements are matched
// as search terms with length < 3 are discarded
const filteredTermCount = [...searchTerms].filter(
(term) => term.length > 2
).length;
if (
wordList.length !== searchTerms.size &&
wordList.length !== filteredTermCount
)
continue;
// ensure that none of the excluded terms is in the search result
if (
[...excludedTerms].some(
(term) =>
terms[term] === file ||
titleTerms[term] === file ||
(terms[term] || []).includes(file) ||
(titleTerms[term] || []).includes(file)
)
)
break;
// select one (max) score for the file.
const score = Math.max(...wordList.map((w) => scoreMap.get(file).get(w)));
// add result to the result list
results.push([
docNames[file],
titles[file],
"",
null,
score,
filenames[file],
SearchResultKind.text,
]);
}
return results;
},
/**
* helper function to return a node containing the
* search summary for a given text. keywords is a list
* of stemmed words.
*/
makeSearchSummary: (htmlText, keywords, anchor) => {
const text = Search.htmlToText(htmlText, anchor);
if (text === "") return null;
const textLower = text.toLowerCase();
const actualStartPosition = [...keywords]
.map((k) => textLower.indexOf(k.toLowerCase()))
.filter((i) => i > -1)
.slice(-1)[0];
const startWithContext = Math.max(actualStartPosition - 120, 0);
const top = startWithContext === 0 ? "" : "...";
const tail = startWithContext + 240 < text.length ? "..." : "";
let summary = document.createElement("p");
summary.classList.add("context");
summary.textContent = top + text.substr(startWithContext, 240).trim() + tail;
return summary;
},
};
_ready(Search.init);
+154
View File
@@ -0,0 +1,154 @@
/* Highlighting utilities for Sphinx HTML documentation. */
"use strict";
const SPHINX_HIGHLIGHT_ENABLED = true
/**
* highlight a given string on a node by wrapping it in
* span elements with the given class name.
*/
const _highlight = (node, addItems, text, className) => {
if (node.nodeType === Node.TEXT_NODE) {
const val = node.nodeValue;
const parent = node.parentNode;
const pos = val.toLowerCase().indexOf(text);
if (
pos >= 0 &&
!parent.classList.contains(className) &&
!parent.classList.contains("nohighlight")
) {
let span;
const closestNode = parent.closest("body, svg, foreignObject");
const isInSVG = closestNode && closestNode.matches("svg");
if (isInSVG) {
span = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
} else {
span = document.createElement("span");
span.classList.add(className);
}
span.appendChild(document.createTextNode(val.substr(pos, text.length)));
const rest = document.createTextNode(val.substr(pos + text.length));
parent.insertBefore(
span,
parent.insertBefore(
rest,
node.nextSibling
)
);
node.nodeValue = val.substr(0, pos);
/* There may be more occurrences of search term in this node. So call this
* function recursively on the remaining fragment.
*/
_highlight(rest, addItems, text, className);
if (isInSVG) {
const rect = document.createElementNS(
"http://www.w3.org/2000/svg",
"rect"
);
const bbox = parent.getBBox();
rect.x.baseVal.value = bbox.x;
rect.y.baseVal.value = bbox.y;
rect.width.baseVal.value = bbox.width;
rect.height.baseVal.value = bbox.height;
rect.setAttribute("class", className);
addItems.push({ parent: parent, target: rect });
}
}
} else if (node.matches && !node.matches("button, select, textarea")) {
node.childNodes.forEach((el) => _highlight(el, addItems, text, className));
}
};
const _highlightText = (thisNode, text, className) => {
let addItems = [];
_highlight(thisNode, addItems, text, className);
addItems.forEach((obj) =>
obj.parent.insertAdjacentElement("beforebegin", obj.target)
);
};
/**
* Small JavaScript module for the documentation.
*/
const SphinxHighlight = {
/**
* highlight the search words provided in localstorage in the text
*/
highlightSearchWords: () => {
if (!SPHINX_HIGHLIGHT_ENABLED) return; // bail if no highlight
// get and clear terms from localstorage
const url = new URL(window.location);
const highlight =
localStorage.getItem("sphinx_highlight_terms")
|| url.searchParams.get("highlight")
|| "";
localStorage.removeItem("sphinx_highlight_terms")
url.searchParams.delete("highlight");
window.history.replaceState({}, "", url);
// get individual terms from highlight string
const terms = highlight.toLowerCase().split(/\s+/).filter(x => x);
if (terms.length === 0) return; // nothing to do
// There should never be more than one element matching "div.body"
const divBody = document.querySelectorAll("div.body");
const body = divBody.length ? divBody[0] : document.querySelector("body");
window.setTimeout(() => {
terms.forEach((term) => _highlightText(body, term, "highlighted"));
}, 10);
const searchBox = document.getElementById("searchbox");
if (searchBox === null) return;
searchBox.appendChild(
document
.createRange()
.createContextualFragment(
'<p class="highlight-link">' +
'<a href="javascript:SphinxHighlight.hideSearchWords()">' +
_("Hide Search Matches") +
"</a></p>"
)
);
},
/**
* helper function to hide the search marks again
*/
hideSearchWords: () => {
document
.querySelectorAll("#searchbox .highlight-link")
.forEach((el) => el.remove());
document
.querySelectorAll("span.highlighted")
.forEach((el) => el.classList.remove("highlighted"));
localStorage.removeItem("sphinx_highlight_terms")
},
initEscapeListener: () => {
// only install a listener if it is really needed
if (!DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS) return;
document.addEventListener("keydown", (event) => {
// bail for input elements
if (BLACKLISTED_KEY_CONTROL_ELEMENTS.has(document.activeElement.tagName)) return;
// bail with special keys
if (event.shiftKey || event.altKey || event.ctrlKey || event.metaKey) return;
if (DOCUMENTATION_OPTIONS.ENABLE_SEARCH_SHORTCUTS && (event.key === "Escape")) {
SphinxHighlight.hideSearchWords();
event.preventDefault();
}
});
},
};
_ready(() => {
/* Do not call highlightSearchWords() when we are on the search page.
* It will highlight words from the *previous* search query.
*/
if (typeof Search === "undefined") SphinxHighlight.highlightSearchWords();
SphinxHighlight.initEscapeListener();
});
+102
View File
@@ -0,0 +1,102 @@
<!DOCTYPE html>
<html class="writer-html5" lang="en" data-content_root="./">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Index &mdash; Argenta 1.1.1 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=b86133f3" />
<link rel="stylesheet" type="text/css" href="_static/css/theme.css?v=e59714d7" />
<script src="_static/jquery.js?v=5d32c60e"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js?v=2cd50e6c"></script>
<script src="_static/documentation_options.js?v=58fbf978"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/js/theme.js"></script>
<link rel="index" title="Index" href="#" />
<link rel="search" title="Search" href="search.html" />
</head>
<body class="wy-body-for-nav">
<div class="wy-grid-for-nav">
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
<div class="wy-side-scroll">
<div class="wy-side-nav-search" >
<a href="index.html" class="icon icon-home">
Argenta
</a>
<div role="search">
<form id="rtd-search-form" class="wy-form" action="search.html" method="get">
<input type="text" name="q" placeholder="Search docs" aria-label="Search docs" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
</div><div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="Navigation menu">
<!-- Local TOC -->
<div class="local-toc"></div>
</div>
</div>
</nav>
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap"><nav class="wy-nav-top" aria-label="Mobile navigation menu" >
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
<a href="index.html">Argenta</a>
</nav>
<div class="wy-nav-content">
<div class="rst-content">
<div role="navigation" aria-label="Page navigation">
<ul class="wy-breadcrumbs">
<li><a href="index.html" class="icon icon-home" aria-label="Home"></a></li>
<li class="breadcrumb-item active">Index</li>
<li class="wy-breadcrumbs-aside">
</li>
</ul>
<hr/>
</div>
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
<div itemprop="articleBody">
<h1 id="index">Index</h1>
<div class="genindex-jumpbox">
</div>
</div>
</div>
<footer>
<hr/>
<div role="contentinfo">
<p>&#169; Copyright 2025, kolo.</p>
</div>
Built with <a href="https://www.sphinx-doc.org/">Sphinx</a> using a
<a href="https://github.com/readthedocs/sphinx_rtd_theme">theme</a>
provided by <a href="https://readthedocs.org">Read the Docs</a>.
</footer>
</div>
</div>
</section>
</div>
<script>
jQuery(function () {
SphinxRtdTheme.Navigation.enable(true);
});
</script>
</body>
</html>
+109
View File
@@ -0,0 +1,109 @@
<!DOCTYPE html>
<html class="writer-html5" lang="en" data-content_root="./">
<head>
<meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Argenta documentation &mdash; Argenta 1.1.1 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=b86133f3" />
<link rel="stylesheet" type="text/css" href="_static/css/theme.css?v=e59714d7" />
<script src="_static/jquery.js?v=5d32c60e"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js?v=2cd50e6c"></script>
<script src="_static/documentation_options.js?v=58fbf978"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/js/theme.js"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="search.html" />
</head>
<body class="wy-body-for-nav">
<div class="wy-grid-for-nav">
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
<div class="wy-side-scroll">
<div class="wy-side-nav-search" >
<a href="#" class="icon icon-home">
Argenta
</a>
<div role="search">
<form id="rtd-search-form" class="wy-form" action="search.html" method="get">
<input type="text" name="q" placeholder="Search docs" aria-label="Search docs" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
</div><div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="Navigation menu">
<!-- Local TOC -->
<div class="local-toc"><ul>
<li><a class="reference internal" href="#">Argenta documentation</a></li>
</ul>
</div>
</div>
</div>
</nav>
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap"><nav class="wy-nav-top" aria-label="Mobile navigation menu" >
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
<a href="#">Argenta</a>
</nav>
<div class="wy-nav-content">
<div class="rst-content">
<div role="navigation" aria-label="Page navigation">
<ul class="wy-breadcrumbs">
<li><a href="#" class="icon icon-home" aria-label="Home"></a></li>
<li class="breadcrumb-item active">Argenta documentation</li>
<li class="wy-breadcrumbs-aside">
<a href="_sources/index.rst.txt" rel="nofollow"> View page source</a>
</li>
</ul>
<hr/>
</div>
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
<div itemprop="articleBody">
<section id="argenta-documentation">
<h1>Argenta documentation<a class="headerlink" href="#argenta-documentation" title="Link to this heading"></a></h1>
<p>Add your content using <code class="docutils literal notranslate"><span class="pre">reStructuredText</span></code> syntax. See the
<a class="reference external" href="https://www.sphinx-doc.org/en/master/usage/restructuredtext/index.html">reStructuredText</a>
documentation for details.</p>
<div class="toctree-wrapper compound">
</div>
</section>
</div>
</div>
<footer>
<hr/>
<div role="contentinfo">
<p>&#169; Copyright 2025, kolo.</p>
</div>
Built with <a href="https://www.sphinx-doc.org/">Sphinx</a> using a
<a href="https://github.com/readthedocs/sphinx_rtd_theme">theme</a>
provided by <a href="https://readthedocs.org">Read the Docs</a>.
</footer>
</div>
</div>
</section>
</div>
<script>
jQuery(function () {
SphinxRtdTheme.Navigation.enable(true);
});
</script>
</body>
</html>
BIN
View File
Binary file not shown.
+117
View File
@@ -0,0 +1,117 @@
<!DOCTYPE html>
<html class="writer-html5" lang="en" data-content_root="./">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Search &mdash; Argenta 1.1.1 documentation</title>
<link rel="stylesheet" type="text/css" href="_static/pygments.css?v=b86133f3" />
<link rel="stylesheet" type="text/css" href="_static/css/theme.css?v=e59714d7" />
<script src="_static/jquery.js?v=5d32c60e"></script>
<script src="_static/_sphinx_javascript_frameworks_compat.js?v=2cd50e6c"></script>
<script src="_static/documentation_options.js?v=58fbf978"></script>
<script src="_static/doctools.js?v=9bcbadda"></script>
<script src="_static/sphinx_highlight.js?v=dc90522c"></script>
<script src="_static/js/theme.js"></script>
<script src="_static/searchtools.js"></script>
<script src="_static/language_data.js"></script>
<link rel="index" title="Index" href="genindex.html" />
<link rel="search" title="Search" href="#" />
</head>
<body class="wy-body-for-nav">
<div class="wy-grid-for-nav">
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
<div class="wy-side-scroll">
<div class="wy-side-nav-search" >
<a href="index.html" class="icon icon-home">
Argenta
</a>
<div role="search">
<form id="rtd-search-form" class="wy-form" action="#" method="get">
<input type="text" name="q" placeholder="Search docs" aria-label="Search docs" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
</div><div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="Navigation menu">
<!-- Local TOC -->
<div class="local-toc"></div>
</div>
</div>
</nav>
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap"><nav class="wy-nav-top" aria-label="Mobile navigation menu" >
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
<a href="index.html">Argenta</a>
</nav>
<div class="wy-nav-content">
<div class="rst-content">
<div role="navigation" aria-label="Page navigation">
<ul class="wy-breadcrumbs">
<li><a href="index.html" class="icon icon-home" aria-label="Home"></a></li>
<li class="breadcrumb-item active">Search</li>
<li class="wy-breadcrumbs-aside">
</li>
</ul>
<hr/>
</div>
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
<div itemprop="articleBody">
<noscript>
<div id="fallback" class="admonition warning">
<p class="last">
Please activate JavaScript to enable the search functionality.
</p>
</div>
</noscript>
<div id="search-results">
</div>
</div>
</div>
<footer>
<hr/>
<div role="contentinfo">
<p>&#169; Copyright 2025, kolo.</p>
</div>
Built with <a href="https://www.sphinx-doc.org/">Sphinx</a> using a
<a href="https://github.com/readthedocs/sphinx_rtd_theme">theme</a>
provided by <a href="https://readthedocs.org">Read the Docs</a>.
</footer>
</div>
</div>
</section>
</div>
<script>
jQuery(function () {
SphinxRtdTheme.Navigation.enable(true);
});
</script>
<script>
jQuery(function() { Search.loadIndex("searchindex.js"); });
</script>
<script id="searchindexloader"></script>
</body>
</html>
+1
View File
@@ -0,0 +1 @@
Search.setIndex({"alltitles":{"Argenta documentation":[[0,null]]},"docnames":["index"],"envversion":{"sphinx":65,"sphinx.domains.c":3,"sphinx.domains.changeset":1,"sphinx.domains.citation":1,"sphinx.domains.cpp":9,"sphinx.domains.index":1,"sphinx.domains.javascript":3,"sphinx.domains.math":2,"sphinx.domains.python":4,"sphinx.domains.rst":2,"sphinx.domains.std":2},"filenames":["index.rst"],"indexentries":{},"objects":{},"objnames":{},"objtypes":{},"terms":{"add":0,"content":0,"detail":0,"restructuredtext":0,"see":0,"syntax":0,"us":0,"your":0},"titles":["Argenta documentation"],"titleterms":{"argenta":0,"document":0}})
+27
View File
@@ -0,0 +1,27 @@
# Configuration file for the Sphinx documentation builder.
#
# For the full list of built-in configuration values, see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
# -- Project information -----------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information
project = "Argenta"
copyright = "2025, kolo"
author = "kolo"
release = "1.1.1"
# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
extensions = []
templates_path = ["_templates"]
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
html_theme = "sphinx_rtd_theme"
html_static_path = ["_static"]
+17
View File
@@ -0,0 +1,17 @@
.. Argenta documentation master file, created by
sphinx-quickstart on Sat Oct 11 19:54:43 2025.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
Argenta documentation
=====================
Add your content using ``reStructuredText`` syntax. See the
`reStructuredText <https://www.sphinx-doc.org/en/master/usage/restructuredtext/index.html>`_
documentation for details.
.. toctree::
:maxdepth: 2
:caption: Contents:
+35
View File
@@ -0,0 +1,35 @@
@ECHO OFF
pushd %~dp0
REM Command file for Sphinx documentation
if "%SPHINXBUILD%" == "" (
set SPHINXBUILD=sphinx-build
)
set SOURCEDIR=.
set BUILDDIR=_build
%SPHINXBUILD% >NUL 2>NUL
if errorlevel 9009 (
echo.
echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
echo.installed, then set the SPHINXBUILD environment variable to point
echo.to the full path of the 'sphinx-build' executable. Alternatively you
echo.may add the Sphinx directory to PATH.
echo.
echo.If you don't have Sphinx installed, grab it from
echo.https://www.sphinx-doc.org/
exit /b 1
)
if "%1" == "" goto help
%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
goto end
:help
%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
:end
popd
+2
View File
@@ -0,0 +1,2 @@
sphinx
sphinx-rtd-theme
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

Some files were not shown because too many files have changed in this diff Show More