From 1b9ae9b9e04c2b96771c69e85d9285694ece06dd Mon Sep 17 00:00:00 2001 From: Hugo Saporetti Junior Date: Fri, 3 May 2024 13:04:16 -0300 Subject: [PATCH] line_input bugfixes and improvements --- modules/clitt/bumpver.toml | 2 +- modules/clitt/gradle.properties | 2 +- modules/clitt/src/demo/__init__.py | 4 +- .../clitt/src/demo/application/__init__.py | 4 +- modules/clitt/src/demo/icons/__init__.py | 4 +- .../clitt/src/demo/icons/emojis/__init__.py | 4 +- .../src/demo/icons/font_awesome/__init__.py | 4 +- modules/clitt/src/demo/term/__init__.py | 4 +- modules/clitt/src/demo/tui/__init__.py | 4 +- .../clitt/src/demo/tui/line_input/__init__.py | 4 +- .../demo/tui/line_input/line_input_demo.py | 2 +- .../clitt/src/demo/tui/mchoose/__init__.py | 4 +- .../clitt/src/demo/tui/mdashboard/__init__.py | 4 +- modules/clitt/src/demo/tui/menu/__init__.py | 4 +- modules/clitt/src/demo/tui/minput/__init__.py | 4 +- .../clitt/src/demo/tui/mselect/__init__.py | 4 +- modules/clitt/src/demo/tui/table/__init__.py | 4 +- modules/clitt/src/main/README.md | 323 +++++++++++++++++- modules/clitt/src/main/__init__.py | 4 +- modules/clitt/src/main/clitt/.version | 2 +- modules/clitt/src/main/clitt/__init__.py | 4 +- .../clitt/src/main/clitt/addons/__init__.py | 4 +- .../src/main/clitt/addons/appman/__init__.py | 4 +- .../clitt/addons/appman/templates/__init__.py | 4 +- .../src/main/clitt/addons/widman/__init__.py | 4 +- .../clitt/addons/widman/widgets/__init__.py | 4 +- modules/clitt/src/main/clitt/core/__init__.py | 4 +- .../src/main/clitt/core/exception/__init__.py | 4 +- .../src/main/clitt/core/icons/__init__.py | 4 +- .../main/clitt/core/icons/emojis/__init__.py | 4 +- .../clitt/core/icons/font_awesome/__init__.py | 4 +- .../src/main/clitt/core/term/__init__.py | 4 +- .../clitt/src/main/clitt/core/tui/__init__.py | 4 +- .../clitt/core/tui/line_input/__init__.py | 4 +- .../core/tui/line_input/keyboard_input.py | 34 +- .../clitt/core/tui/line_input/line_input.py | 9 +- .../main/clitt/core/tui/mchoose/__init__.py | 4 +- .../clitt/core/tui/mdashboard/__init__.py | 4 +- .../src/main/clitt/core/tui/menu/__init__.py | 4 +- .../main/clitt/core/tui/minput/__init__.py | 4 +- .../clitt/core/tui/minput/input_validator.py | 2 +- .../main/clitt/core/tui/minput/menu_input.py | 12 +- .../main/clitt/core/tui/mselect/__init__.py | 4 +- .../src/main/clitt/core/tui/table/__init__.py | 4 +- .../clitt/src/main/clitt/utils/__init__.py | 4 +- modules/clitt/src/test/__init__.py | 4 +- 46 files changed, 432 insertions(+), 104 deletions(-) diff --git a/modules/clitt/bumpver.toml b/modules/clitt/bumpver.toml index 82a6d0284..f2b468a29 100644 --- a/modules/clitt/bumpver.toml +++ b/modules/clitt/bumpver.toml @@ -1,5 +1,5 @@ [bumpver] -current_version = "0.9.122" +current_version = "0.9.124" version_pattern = "MAJOR.MINOR.PATCH" commit_message = "++version {old_version} -> {new_version}" commit = false diff --git a/modules/clitt/gradle.properties b/modules/clitt/gradle.properties index bca8e863e..1abae4c4d 100644 --- a/modules/clitt/gradle.properties +++ b/modules/clitt/gradle.properties @@ -1,2 +1,2 @@ -app_version = '0.9.122' +app_version = '0.9.124' app_name = 'hspylib-clitt' diff --git a/modules/clitt/src/demo/__init__.py b/modules/clitt/src/demo/__init__.py index eafa20a1d..a1b9020f9 100644 --- a/modules/clitt/src/demo/__init__.py +++ b/modules/clitt/src/demo/__init__.py @@ -1,6 +1,6 @@ # _*_ coding: utf-8 _*_ # -# hspylib-clitt v0.9.122 +# hspylib-clitt v0.9.124 # # Package: demo """Package initialization.""" @@ -11,4 +11,4 @@ 'term', 'tui' ] -__version__ = '0.9.122' +__version__ = '0.9.124' diff --git a/modules/clitt/src/demo/application/__init__.py b/modules/clitt/src/demo/application/__init__.py index d7e16b7bb..6f63d661b 100644 --- a/modules/clitt/src/demo/application/__init__.py +++ b/modules/clitt/src/demo/application/__init__.py @@ -1,6 +1,6 @@ # _*_ coding: utf-8 _*_ # -# hspylib-clitt v0.9.122 +# hspylib-clitt v0.9.124 # # Package: demo.application """Package initialization.""" @@ -9,4 +9,4 @@ 'application_demo', 'snake' ] -__version__ = '0.9.122' +__version__ = '0.9.124' diff --git a/modules/clitt/src/demo/icons/__init__.py b/modules/clitt/src/demo/icons/__init__.py index e5fdf2eae..e7ef482ba 100644 --- a/modules/clitt/src/demo/icons/__init__.py +++ b/modules/clitt/src/demo/icons/__init__.py @@ -1,6 +1,6 @@ # _*_ coding: utf-8 _*_ # -# hspylib-clitt v0.9.122 +# hspylib-clitt v0.9.124 # # Package: demo.icons """Package initialization.""" @@ -9,4 +9,4 @@ 'emojis', 'font_awesome' ] -__version__ = '0.9.122' +__version__ = '0.9.124' diff --git a/modules/clitt/src/demo/icons/emojis/__init__.py b/modules/clitt/src/demo/icons/emojis/__init__.py index 6934cd27d..0c281f6f5 100644 --- a/modules/clitt/src/demo/icons/emojis/__init__.py +++ b/modules/clitt/src/demo/icons/emojis/__init__.py @@ -1,6 +1,6 @@ # _*_ coding: utf-8 _*_ # -# hspylib-clitt v0.9.122 +# hspylib-clitt v0.9.124 # # Package: demo.icons.emojis """Package initialization.""" @@ -8,4 +8,4 @@ __all__ = [ 'emojis_demo' ] -__version__ = '0.9.122' +__version__ = '0.9.124' diff --git a/modules/clitt/src/demo/icons/font_awesome/__init__.py b/modules/clitt/src/demo/icons/font_awesome/__init__.py index 521aa1bca..aace9df79 100644 --- a/modules/clitt/src/demo/icons/font_awesome/__init__.py +++ b/modules/clitt/src/demo/icons/font_awesome/__init__.py @@ -1,6 +1,6 @@ # _*_ coding: utf-8 _*_ # -# hspylib-clitt v0.9.122 +# hspylib-clitt v0.9.124 # # Package: demo.icons.font_awesome """Package initialization.""" @@ -9,4 +9,4 @@ 'awesome_demo', 'demo_icons' ] -__version__ = '0.9.122' +__version__ = '0.9.124' diff --git a/modules/clitt/src/demo/term/__init__.py b/modules/clitt/src/demo/term/__init__.py index 6a32bcc9c..4bcd07cab 100644 --- a/modules/clitt/src/demo/term/__init__.py +++ b/modules/clitt/src/demo/term/__init__.py @@ -1,6 +1,6 @@ # _*_ coding: utf-8 _*_ # -# hspylib-clitt v0.9.122 +# hspylib-clitt v0.9.124 # # Package: demo.term """Package initialization.""" @@ -8,4 +8,4 @@ __all__ = [ 'terminal_demo' ] -__version__ = '0.9.122' +__version__ = '0.9.124' diff --git a/modules/clitt/src/demo/tui/__init__.py b/modules/clitt/src/demo/tui/__init__.py index 53350f9b1..d099ef2f5 100755 --- a/modules/clitt/src/demo/tui/__init__.py +++ b/modules/clitt/src/demo/tui/__init__.py @@ -1,6 +1,6 @@ # _*_ coding: utf-8 _*_ # -# hspylib-clitt v0.9.122 +# hspylib-clitt v0.9.124 # # Package: demo.tui """Package initialization.""" @@ -16,4 +16,4 @@ 'tui_preferences_demo', 'tui_screen_demo' ] -__version__ = '0.9.122' +__version__ = '0.9.124' diff --git a/modules/clitt/src/demo/tui/line_input/__init__.py b/modules/clitt/src/demo/tui/line_input/__init__.py index d5e1b087d..44d6d44d3 100644 --- a/modules/clitt/src/demo/tui/line_input/__init__.py +++ b/modules/clitt/src/demo/tui/line_input/__init__.py @@ -1,6 +1,6 @@ # _*_ coding: utf-8 _*_ # -# hspylib-clitt v0.9.122 +# hspylib-clitt v0.9.124 # # Package: demo.tui.line_input """Package initialization.""" @@ -8,4 +8,4 @@ __all__ = [ 'line_input_demo' ] -__version__ = '0.9.122' +__version__ = '0.9.124' diff --git a/modules/clitt/src/demo/tui/line_input/line_input_demo.py b/modules/clitt/src/demo/tui/line_input/line_input_demo.py index 1f27e5c2f..db6ca2759 100644 --- a/modules/clitt/src/demo/tui/line_input/line_input_demo.py +++ b/modules/clitt/src/demo/tui/line_input/line_input_demo.py @@ -21,7 +21,7 @@ hist = ["Hugo", "Joao", "Koko", "Hugo", "Koko"] KeyboardInput.preload_history(hist) print("-=" * 30) - while (i := line_input("What is it? ", VtColor.YELLOW, VtColor.GREEN, True)) not in ["bye", "", None]: + while (i := line_input("What is it? ", "Input your name", VtColor.YELLOW, VtColor.GREEN, True)) not in ["bye", "", None]: if isinstance(i, Keyboard): print("PTT", i) else: diff --git a/modules/clitt/src/demo/tui/mchoose/__init__.py b/modules/clitt/src/demo/tui/mchoose/__init__.py index 77fa95ec7..5a0d28da5 100644 --- a/modules/clitt/src/demo/tui/mchoose/__init__.py +++ b/modules/clitt/src/demo/tui/mchoose/__init__.py @@ -1,6 +1,6 @@ # _*_ coding: utf-8 _*_ # -# hspylib-clitt v0.9.122 +# hspylib-clitt v0.9.124 # # Package: demo.tui.mchoose """Package initialization.""" @@ -8,4 +8,4 @@ __all__ = [ 'menu_choose_demo' ] -__version__ = '0.9.122' +__version__ = '0.9.124' diff --git a/modules/clitt/src/demo/tui/mdashboard/__init__.py b/modules/clitt/src/demo/tui/mdashboard/__init__.py index 8defe1e8a..b67a23f51 100644 --- a/modules/clitt/src/demo/tui/mdashboard/__init__.py +++ b/modules/clitt/src/demo/tui/mdashboard/__init__.py @@ -1,6 +1,6 @@ # _*_ coding: utf-8 _*_ # -# hspylib-clitt v0.9.122 +# hspylib-clitt v0.9.124 # # Package: demo.tui.mdashboard """Package initialization.""" @@ -8,4 +8,4 @@ __all__ = [ 'menu_dashboard_demo' ] -__version__ = '0.9.122' +__version__ = '0.9.124' diff --git a/modules/clitt/src/demo/tui/menu/__init__.py b/modules/clitt/src/demo/tui/menu/__init__.py index f1eeec430..46ce640af 100644 --- a/modules/clitt/src/demo/tui/menu/__init__.py +++ b/modules/clitt/src/demo/tui/menu/__init__.py @@ -1,6 +1,6 @@ # _*_ coding: utf-8 _*_ # -# hspylib-clitt v0.9.122 +# hspylib-clitt v0.9.124 # # Package: demo.tui.menu """Package initialization.""" @@ -8,4 +8,4 @@ __all__ = [ 'tui_menu_demo' ] -__version__ = '0.9.122' +__version__ = '0.9.124' diff --git a/modules/clitt/src/demo/tui/minput/__init__.py b/modules/clitt/src/demo/tui/minput/__init__.py index 5bda7d333..5b5c88c5c 100644 --- a/modules/clitt/src/demo/tui/minput/__init__.py +++ b/modules/clitt/src/demo/tui/minput/__init__.py @@ -1,6 +1,6 @@ # _*_ coding: utf-8 _*_ # -# hspylib-clitt v0.9.122 +# hspylib-clitt v0.9.124 # # Package: demo.tui.minput """Package initialization.""" @@ -9,4 +9,4 @@ 'menu_input_demo', 'menu_input_tokens_demo' ] -__version__ = '0.9.122' +__version__ = '0.9.124' diff --git a/modules/clitt/src/demo/tui/mselect/__init__.py b/modules/clitt/src/demo/tui/mselect/__init__.py index 310049f9c..a1c615088 100644 --- a/modules/clitt/src/demo/tui/mselect/__init__.py +++ b/modules/clitt/src/demo/tui/mselect/__init__.py @@ -1,6 +1,6 @@ # _*_ coding: utf-8 _*_ # -# hspylib-clitt v0.9.122 +# hspylib-clitt v0.9.124 # # Package: demo.tui.mselect """Package initialization.""" @@ -8,4 +8,4 @@ __all__ = [ 'menu_select_demo' ] -__version__ = '0.9.122' +__version__ = '0.9.124' diff --git a/modules/clitt/src/demo/tui/table/__init__.py b/modules/clitt/src/demo/tui/table/__init__.py index cd34d7210..61a860e28 100644 --- a/modules/clitt/src/demo/tui/table/__init__.py +++ b/modules/clitt/src/demo/tui/table/__init__.py @@ -1,6 +1,6 @@ # _*_ coding: utf-8 _*_ # -# hspylib-clitt v0.9.122 +# hspylib-clitt v0.9.124 # # Package: demo.tui.table """Package initialization.""" @@ -8,4 +8,4 @@ __all__ = [ 'table_demo' ] -__version__ = '0.9.122' +__version__ = '0.9.124' diff --git a/modules/clitt/src/main/README.md b/modules/clitt/src/main/README.md index 2ba4bfbd1..9681cccc7 100644 --- a/modules/clitt/src/main/README.md +++ b/modules/clitt/src/main/README.md @@ -1,10 +1,321 @@ -# HsPyLib - CLI Terminal Tools + -## Create professional CLI applications +# HomeSetup Python Library +> +> Because your Python code is not JUST a script ! -[![License](https://badgen.net/badge/license/MIT/gray)](LICENSE.md) -[![Release](https://badgen.net/badge/release/v0.9.122/gray)](CHANGELOG.md#unreleased) -[![PyPi](https://badgen.net/badge/icon/python?icon=pypi&label)](https://pypi.org/project/hspylib-clitt) -[![GitHub](https://badgen.net/badge/icon/github?icon=github&label)](https://github.com/yorevs/hspylib) +[![PyPi](https://badgen.net/badge/icon/python?icon=pypi&label)](https://pypi.org/project/hspylib) [![Gitter](https://badgen.net/badge/icon/gitter?icon=gitter&label)](https://gitter.im/hspylib/community) [![Donate](https://badgen.net/badge/paypal/donate/yellow)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=J5CDEFLF6M3H4) +[![License](https://badgen.net/badge/license/MIT/gray)](LICENSE.md) +[![Release](https://badgen.net/badge/release/v0.9.124/gray)](docs/CHANGELOG.md#unreleased) +[![build-and-test](https://github.com/yorevs/hspylib/actions/workflows/build-and-test.yml/badge.svg)](https://github.com/yorevs/hspylib/actions/workflows/build-and-test.yml) + +HsPyLib is not just a Python library; it's a gateway to elevating your programming experience to new heights. Built on +established principles like **SOLID**, **DRY** (Don't Repeat Yourself), **KISS** (Keep It Simple, Stupid), and **YAGNI** +(You Ain’t Gonna Need It), HsPyLib offers a wealth of frameworks and features. It empowers you to craft sophisticated +Python3 applications, adhering to the philosophy that code should not merely be a simple script - it should be a part +of the 'PYCSNBASS' (Python Code Should Not Be A Simple Script) mindset. + +> This project is a part of the [HomeSetup](https://github.com/yorevs/homesetup) project. + +## Key Features + +- Seamless installation process. +- Application manager offering a helpful scaffold for Python applications. +- Widgets manager for running both 'built-in' and custom Python widgets. +- Improved TUI (Text User Interface) helpers and input methods to enhance your User Experience with terminal applications. +- CRUD (Create, Read, Update, Delete) framework aiding with databases, repositories, and services. +- HTTP request helpers for simplified communication. +- Support for enabling Properties and AppConfigs using popular extensions like .properties, toml, yml, and more. +- Code rigorously tested and consistently adhering to Pylint standards. +- Utilizes the Gradle build system with numerous extensions. +- Diverse set of demos to facilitate a deeper understanding of the library. +- AI model integrations (currently supporting OpenAI). + +> Create beautiful menu-select inputs + +```python +class SelectableItem: + def __init__(self, name: str, value: str): + self.name = name + self.value = value + + def __str__(self): + return f"Name: {self.name} Value: {self.value}" + + def __repr__(self): + return str(self) + + +if __name__ == "__main__": + quantity = 22 + digits = len(str(quantity)) + it = [SelectableItem(f"Item-{n:>0{digits}}", f"Value-{n:>0{digits}}") for n in range(1, quantity)] + sel = mselect(it) + print(str(sel)) +``` + +![MenuSelect](https://iili.io/HYBFh74.png "MenuSelect") + +> And create beautiful menu-choose inputs + +```python +class ChooseableItem: + def __init__(self, name: str, value: str): + self.name = name + self.value = value + + def __str__(self): + return f"Name: {self.name} Value: {self.value}" + + def __repr__(self): + return str(self) + + +if __name__ == "__main__": + quantity = 22 + digits = len(str(quantity)) + it = [ChooseableItem(f"Item-{n:>0{digits}}", f"Value-{n:>0{digits}}") for n in range(1, quantity)] + sel = mchoose(it, [n % 2 == 0 for n in range(1, quantity)]) + print(str(sel)) +``` + +![MenuChoose](https://iili.io/HYBFwp2.png "MenuChoose") + +> And also, create beautiful form inputs + +```python +from clitt.core.tui.minput.input_validator import InputValidator +from clitt.core.tui.minput.minput import MenuInput, minput + +if __name__ == "__main__": + # fmt: off + form_fields = MenuInput.builder() \ + .field() \ + .label('letters') \ + .validator(InputValidator.letters()) \ + .build() \ + .field() \ + .label('word') \ + .validator(InputValidator.words()) \ + .build() \ + .field() \ + .label('number') \ + .validator(InputValidator.numbers()) \ + .min_max_length(1, 4) \ + .build() \ + .field() \ + .label('masked') \ + .itype('masked') \ + .value('|##::##::## @@') \ + .build() \ + .field() \ + .label('selectable') \ + .itype('select') \ + .value('one|two|three') \ + .build() \ + .field() \ + .label('checkbox') \ + .itype('checkbox') \ + .build() \ + .field() \ + .label('password') \ + .itype('password') \ + .validator(InputValidator.anything()) \ + .min_max_length(4, 8) \ + .build() \ + .field() \ + .label('read-only') \ + .access_type('read-only') \ + .value('READ-ONLY') \ + .build() \ + .build() + # fmt: on + + result = minput(form_fields) + print(result.__dict__ if result else "None") +``` + +![MenuInput](https://iili.io/HYBFVrG.png "MenuInput") + +> Or even, create nice dashboards: + +```python +if __name__ == "__main__": + # fmt: off + dashboard_items = MenuDashBoard.builder() \ + .item() \ + .icon(DashboardIcons.POWER) \ + .tooltip('Do something') \ + .on_trigger(lambda: print('Something')) \ + .build() \ + .item() \ + .icon(DashboardIcons.MOVIE) \ + .tooltip('Another something') \ + .on_trigger(lambda: print('Another')) \ + .build() \ + .item() \ + .icon(DashboardIcons.NOTIFICATION) \ + .tooltip('Notify something') \ + .on_trigger(lambda: print('Notification')) \ + .build() \ + .item() \ + .icon(DashboardIcons.LIST) \ + .tooltip('List everything') \ + .on_trigger(lambda: print('List')) \ + .build() \ + .item() \ + .icon(DashboardIcons.DATABASE) \ + .tooltip('Database console') \ + .on_trigger(lambda: print('Database')) \ + .build() \ + .item() \ + .icon(DashboardIcons.EXIT) \ + .tooltip('Exit application') \ + .on_trigger(lambda: print('Exit')) \ + .build() \ + .build() + # fmt: on + + result = mdashboard(dashboard_items) +``` + +![MenuDashboard](https://iili.io/HYBFX2f.png "MenuDashboard") + +> And even more, create beautiful menus ! + +```bash +if __name__ == "__main__": + # fmt: off + main_menu = TUIMenuFactory \ + .create_main_menu('TUI Main Menu', tooltip='Test Terminal UI Menus') \ + .with_item('Sub-Menu-1') \ + .with_action("DO IT 1", "Let's do it") \ + .on_trigger(lambda x: print("ACTION 1", x)) \ + .with_view("Just a View 1", "Show the view 1") \ + .on_render("MY BEAUTIFUL VIEW 1") \ + .with_action("Back", "Back to the previous menu") \ + .on_trigger(TUIMenuUi.back) \ + .then() \ + .with_item('Sub-Menu-2') \ + .with_action("DO IT 2", "Let's do it too") \ + .on_trigger(lambda x: print("ACTION 2", x)) \ + .with_view("Just a View 2", "Show the view 2") \ + .on_render("MY BEAUTIFUL VIEW 2") \ + .with_action("Back", "Back to the previous menu") \ + .on_trigger(TUIMenuUi.back) \ + .then() \ + .then() \ + .build() + # fmt: on + + TUIMenuUi(main_menu, "TUI Main Menu").execute() +``` + +![Menus](https://iili.io/JAGQJkJ.png "Menus") + +## PyPi Modules + +- [A Pivotal Cloud Foundry](https://pypi.org/project/hspylib-cfman) application tool. +- [A CLI Terminal](https://pypi.org/project/hspylib-clitt) tools. +- [Datasource](https://pypi.org/project/hspylib-datasource) framework. +- [A Firebase](https://pypi.org/project/hspylib-firebase) integration tool. +- [A PyQt](https://pypi.org/project/hspylib-hqt) application framework. +- [Core](https://pypi.org/project/hspylib) HomeSetup library. +- [A Kafka manager](https://pypi.org/project/hspylib-kafman) application tool. +- [A System Settings](https://pypi.org/project/hspylib-kafman) application tool. +- [A Vault](https://pypi.org/project/hspylib-vault) application tool. + +## Installation + +### Requirements + +#### Python + +- Python 3.10 and higher + +#### Operating Systems + +- Darwin + + High Sierra and higher +- Linux + + Ubuntu 16 and higher + + CentOS 7 and higher + + Fedora 31 and higher + +You may want to install HsPyLib on other OS's and it will probably work, but there are no guarantees that it +**WILL ACTUALLY WORK**. + +#### Required software + +The following software are required: + +- Git (To clone the github repository) +- Gradle (To build the HsPyLib project) + +There are some python dependencies, but they will be automatically downloaded when the build runs. + +### PyPi + +To install HsPyLib from PyPi issue the command: + +`# python3 -m pip install hspylib` + +To upgrade HsPyLib use the command: + +`# python3 -m pip install hspylib --upgrade` + +Additional modules that can also be installed: + +- [CLItt](https://pypi.org/project/hspylib-clitt) : `# python3 -m pip install hspylib-clitt` to install HsPyLib CLI terminal tools. +- [Firebase](https://pypi.org/project/hspylib-firebase) : `# python3 -m pip install hspylib-firebase` to install HsPyLib Firebase application. +- [Vault](https://pypi.org/project/hspylib-vault) : `# python3 -m pip install hspylib-vault` to install HsPyLib Vault application. +- [CFMan](https://pypi.org/project/hspylib-cfman) : `# python3 -m pip install hspylib-cfman` to install CloudFoundry manager. +- [Kafman](https://pypi.org/project/hspylib-kafman) : `# python3 -m pip install hspylib-kafman` to install Kafka manager. +- [Datasource](https://pypi.org/project/hspylib-datasource) : `# python3 -m pip install hspylib-datasource` to install datasource helpers. +- [HQT](https://pypi.org/project/hspylib-hqt) : `# python3 -m pip install hspylib-hqt` to install HsPyLib PyQt framework. + +### GitHub + +To clone HsPyLib into your local machine type the command: + +`# git clone https://github.com/yorevs/hspylib.git` + +## Documentation + +The API documentation can be found [here](docs/api/index.html) + +## Support + +> Your support and contributions are greatly appreciated in helping us improve and enhance HomeSetup. Together, we can +make it even better! + +You can support HomeSetup by [donating](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=J5CDEFLF6M3H4) +or contributing code. Feel free to contact me for further details. When making code contributions, please make sure to +review our [guidelines](docs/CONTRIBUTING.md) and adhere to our [code of conduct](docs/CODE_OF_CONDUCT.md). + +[!["Buy Me A Coffee"](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://www.buymeacoffee.com/yorevs) + +You can also sponsor it by using our [GitHub Sponsors](https://github.com/sponsors/yorevs) page. + +This project is already supported by: + + + + + +Thank you <3 !! + +## Known Issues + +- [In-Progress] We are aware that there is a problems when using python@3.12 and we are already working on a fix. + +## Contacts + +- Documentation: [API](docs/api/index.html) +- License: [MIT](LICENSE.md) +- Issue tracker: [HSPyLib-Issues](https://github.com/yorevs/hspylib/issues) +- Official chat: [HSPyLib](https://gitter.im/hspylib/community) +- Contact: [yorevs](https://www.reddit.com/user/yorevs) +- Mailto: [yorevs](mailto:yorevs@hotmail.com) diff --git a/modules/clitt/src/main/__init__.py b/modules/clitt/src/main/__init__.py index 540f73e52..880e5475d 100644 --- a/modules/clitt/src/main/__init__.py +++ b/modules/clitt/src/main/__init__.py @@ -1,6 +1,6 @@ # _*_ coding: utf-8 _*_ # -# hspylib-clitt v0.9.122 +# hspylib-clitt v0.9.124 # # Package: main """Package initialization.""" @@ -8,4 +8,4 @@ __all__ = [ 'clitt' ] -__version__ = '0.9.122' +__version__ = '0.9.124' diff --git a/modules/clitt/src/main/clitt/.version b/modules/clitt/src/main/clitt/.version index cc7df9884..fc707b65f 100644 --- a/modules/clitt/src/main/clitt/.version +++ b/modules/clitt/src/main/clitt/.version @@ -1 +1 @@ -0.9.122 +0.9.124 diff --git a/modules/clitt/src/main/clitt/__init__.py b/modules/clitt/src/main/clitt/__init__.py index 954293b47..af2f52f66 100644 --- a/modules/clitt/src/main/clitt/__init__.py +++ b/modules/clitt/src/main/clitt/__init__.py @@ -1,6 +1,6 @@ # _*_ coding: utf-8 _*_ # -# hspylib-clitt v0.9.122 +# hspylib-clitt v0.9.124 # # Package: main.clitt """Package initialization.""" @@ -10,4 +10,4 @@ 'core', 'utils' ] -__version__ = '0.9.122' +__version__ = '0.9.124' diff --git a/modules/clitt/src/main/clitt/addons/__init__.py b/modules/clitt/src/main/clitt/addons/__init__.py index 6064b9e5b..c5fb4ae6a 100644 --- a/modules/clitt/src/main/clitt/addons/__init__.py +++ b/modules/clitt/src/main/clitt/addons/__init__.py @@ -1,6 +1,6 @@ # _*_ coding: utf-8 _*_ # -# hspylib-clitt v0.9.122 +# hspylib-clitt v0.9.124 # # Package: main.clitt.addons """Package initialization.""" @@ -9,4 +9,4 @@ 'appman', 'widman' ] -__version__ = '0.9.122' +__version__ = '0.9.124' diff --git a/modules/clitt/src/main/clitt/addons/appman/__init__.py b/modules/clitt/src/main/clitt/addons/appman/__init__.py index 23b41a826..182876cd7 100644 --- a/modules/clitt/src/main/clitt/addons/appman/__init__.py +++ b/modules/clitt/src/main/clitt/addons/appman/__init__.py @@ -1,6 +1,6 @@ # _*_ coding: utf-8 _*_ # -# hspylib-clitt v0.9.122 +# hspylib-clitt v0.9.124 # # Package: main.clitt.addons.appman """Package initialization.""" @@ -10,4 +10,4 @@ 'appman_enums', 'templates' ] -__version__ = '0.9.122' +__version__ = '0.9.124' diff --git a/modules/clitt/src/main/clitt/addons/appman/templates/__init__.py b/modules/clitt/src/main/clitt/addons/appman/templates/__init__.py index 4a2a3619d..2327df91f 100644 --- a/modules/clitt/src/main/clitt/addons/appman/templates/__init__.py +++ b/modules/clitt/src/main/clitt/addons/appman/templates/__init__.py @@ -1,6 +1,6 @@ # _*_ coding: utf-8 _*_ # -# hspylib-clitt v0.9.122 +# hspylib-clitt v0.9.124 # # Package: main.clitt.addons.appman.templates """Package initialization.""" @@ -8,4 +8,4 @@ __all__ = [ ] -__version__ = '0.9.122' +__version__ = '0.9.124' diff --git a/modules/clitt/src/main/clitt/addons/widman/__init__.py b/modules/clitt/src/main/clitt/addons/widman/__init__.py index 0b027b689..74f49415f 100644 --- a/modules/clitt/src/main/clitt/addons/widman/__init__.py +++ b/modules/clitt/src/main/clitt/addons/widman/__init__.py @@ -1,6 +1,6 @@ # _*_ coding: utf-8 _*_ # -# hspylib-clitt v0.9.122 +# hspylib-clitt v0.9.124 # # Package: main.clitt.addons.widman """Package initialization.""" @@ -11,4 +11,4 @@ 'widgets', 'widman' ] -__version__ = '0.9.122' +__version__ = '0.9.124' diff --git a/modules/clitt/src/main/clitt/addons/widman/widgets/__init__.py b/modules/clitt/src/main/clitt/addons/widman/widgets/__init__.py index d183c321d..a139ace36 100644 --- a/modules/clitt/src/main/clitt/addons/widman/widgets/__init__.py +++ b/modules/clitt/src/main/clitt/addons/widman/widgets/__init__.py @@ -1,6 +1,6 @@ # _*_ coding: utf-8 _*_ # -# hspylib-clitt v0.9.122 +# hspylib-clitt v0.9.124 # # Package: main.clitt.addons.widman.widgets """Package initialization.""" @@ -11,4 +11,4 @@ 'widget_send_msg', 'widget_time_calc' ] -__version__ = '0.9.122' +__version__ = '0.9.124' diff --git a/modules/clitt/src/main/clitt/core/__init__.py b/modules/clitt/src/main/clitt/core/__init__.py index 5b68934d0..2a83be7c1 100644 --- a/modules/clitt/src/main/clitt/core/__init__.py +++ b/modules/clitt/src/main/clitt/core/__init__.py @@ -1,6 +1,6 @@ # _*_ coding: utf-8 _*_ # -# hspylib-clitt v0.9.122 +# hspylib-clitt v0.9.124 # # Package: main.clitt.core """Package initialization.""" @@ -12,4 +12,4 @@ 'term', 'tui' ] -__version__ = '0.9.122' +__version__ = '0.9.124' diff --git a/modules/clitt/src/main/clitt/core/exception/__init__.py b/modules/clitt/src/main/clitt/core/exception/__init__.py index 5d4fa2905..63735bb4f 100644 --- a/modules/clitt/src/main/clitt/core/exception/__init__.py +++ b/modules/clitt/src/main/clitt/core/exception/__init__.py @@ -1,6 +1,6 @@ # _*_ coding: utf-8 _*_ # -# hspylib-clitt v0.9.122 +# hspylib-clitt v0.9.124 # # Package: main.clitt.core.exception """Package initialization.""" @@ -8,4 +8,4 @@ __all__ = [ 'exceptions' ] -__version__ = '0.9.122' +__version__ = '0.9.124' diff --git a/modules/clitt/src/main/clitt/core/icons/__init__.py b/modules/clitt/src/main/clitt/core/icons/__init__.py index 1ce899379..325422321 100644 --- a/modules/clitt/src/main/clitt/core/icons/__init__.py +++ b/modules/clitt/src/main/clitt/core/icons/__init__.py @@ -1,6 +1,6 @@ # _*_ coding: utf-8 _*_ # -# hspylib-clitt v0.9.122 +# hspylib-clitt v0.9.124 # # Package: main.clitt.core.icons """Package initialization.""" @@ -9,4 +9,4 @@ 'emojis', 'font_awesome' ] -__version__ = '0.9.122' +__version__ = '0.9.124' diff --git a/modules/clitt/src/main/clitt/core/icons/emojis/__init__.py b/modules/clitt/src/main/clitt/core/icons/emojis/__init__.py index bf6505a57..b297b6b1b 100644 --- a/modules/clitt/src/main/clitt/core/icons/emojis/__init__.py +++ b/modules/clitt/src/main/clitt/core/icons/emojis/__init__.py @@ -1,6 +1,6 @@ # _*_ coding: utf-8 _*_ # -# hspylib-clitt v0.9.122 +# hspylib-clitt v0.9.124 # # Package: main.clitt.core.icons.emojis """Package initialization.""" @@ -9,4 +9,4 @@ 'emojis', 'face_smiling' ] -__version__ = '0.9.122' +__version__ = '0.9.124' diff --git a/modules/clitt/src/main/clitt/core/icons/font_awesome/__init__.py b/modules/clitt/src/main/clitt/core/icons/font_awesome/__init__.py index 94e5503e4..f3b627425 100644 --- a/modules/clitt/src/main/clitt/core/icons/font_awesome/__init__.py +++ b/modules/clitt/src/main/clitt/core/icons/font_awesome/__init__.py @@ -1,6 +1,6 @@ # _*_ coding: utf-8 _*_ # -# hspylib-clitt v0.9.122 +# hspylib-clitt v0.9.124 # # Package: main.clitt.core.icons.font_awesome """Package initialization.""" @@ -15,4 +15,4 @@ 'trickplay_icons', 'widget_icons' ] -__version__ = '0.9.122' +__version__ = '0.9.124' diff --git a/modules/clitt/src/main/clitt/core/term/__init__.py b/modules/clitt/src/main/clitt/core/term/__init__.py index ac542cb97..e359b62bc 100644 --- a/modules/clitt/src/main/clitt/core/term/__init__.py +++ b/modules/clitt/src/main/clitt/core/term/__init__.py @@ -1,6 +1,6 @@ # _*_ coding: utf-8 _*_ # -# hspylib-clitt v0.9.122 +# hspylib-clitt v0.9.124 # # Package: main.clitt.core.term """Package initialization.""" @@ -11,4 +11,4 @@ 'screen', 'terminal' ] -__version__ = '0.9.122' +__version__ = '0.9.124' diff --git a/modules/clitt/src/main/clitt/core/tui/__init__.py b/modules/clitt/src/main/clitt/core/tui/__init__.py index 84b119782..66eccd359 100644 --- a/modules/clitt/src/main/clitt/core/tui/__init__.py +++ b/modules/clitt/src/main/clitt/core/tui/__init__.py @@ -1,6 +1,6 @@ # _*_ coding: utf-8 _*_ # -# hspylib-clitt v0.9.122 +# hspylib-clitt v0.9.124 # # Package: main.clitt.core.tui """Package initialization.""" @@ -17,4 +17,4 @@ 'tui_component', 'tui_preferences' ] -__version__ = '0.9.122' +__version__ = '0.9.124' diff --git a/modules/clitt/src/main/clitt/core/tui/line_input/__init__.py b/modules/clitt/src/main/clitt/core/tui/line_input/__init__.py index ce5ac3357..9b9e8111b 100644 --- a/modules/clitt/src/main/clitt/core/tui/line_input/__init__.py +++ b/modules/clitt/src/main/clitt/core/tui/line_input/__init__.py @@ -1,6 +1,6 @@ # _*_ coding: utf-8 _*_ # -# hspylib-clitt v0.9.122 +# hspylib-clitt v0.9.124 # # Package: main.clitt.core.tui.line_input """Package initialization.""" @@ -9,4 +9,4 @@ 'keyboard_input', 'line_input' ] -__version__ = '0.9.122' +__version__ = '0.9.124' diff --git a/modules/clitt/src/main/clitt/core/tui/line_input/keyboard_input.py b/modules/clitt/src/main/clitt/core/tui/line_input/keyboard_input.py index 23ea821a1..12a40a8f4 100644 --- a/modules/clitt/src/main/clitt/core/tui/line_input/keyboard_input.py +++ b/modules/clitt/src/main/clitt/core/tui/line_input/keyboard_input.py @@ -91,17 +91,19 @@ def _redo(cls) -> Optional[str]: def __init__( self, prompt: str = "", + placeholder: str = "", prompt_color: VtColor = VtColor.NC, text_color: VtColor = VtColor.NC, navbar_enable: bool = False, ): super().__init__(prompt) + self._placeholder: str = placeholder self._prompt_color: VtColor = prompt_color self._text_color: VtColor = text_color self._navbar_enable: bool = navbar_enable self._input_index: int = 0 self._input_text: str = "" - self._tab_complete: str = "" + self._suggestion: str = "" self._HISTORY[-1] = "" self._HIST_INDEX = max(0, len(self._HISTORY) - 1) @@ -126,6 +128,7 @@ def execute(self) -> Optional[str | Keyboard]: elif keypress == Keyboard.VK_ESC: self._input_text = None + self._terminal.cursor.erase(Direction.RIGHT) self.writeln("%NC%") return self._input_text @@ -153,9 +156,13 @@ def render(self) -> None: Terminal.set_show_cursor(False) self.cursor.restore() self.write(f"{self._prompt_color.placeholder}{self.title}{self._text_color.placeholder}") - self.write(self._input_text) - # Write the tab complete option - self._write_hint() + if self._input_text: + self.write(self._input_text) + self._write_suggestion() + else: + self.write(f"%GRAY%{self._placeholder}%NC%") + self._terminal.cursor.erase(Direction.DOWN) + self.cursor.move(len(self._placeholder), Direction.LEFT) self._re_render = False self._set_cursor_pos() Terminal.set_show_cursor() @@ -170,11 +177,11 @@ def handle_keypress(self) -> Keyboard: if self._input_index > 0: self._input_index = max(0, self._input_index - 1) self._update_input( - self._input_text[: self._input_index] + self._input_text[1 + self._input_index :] + self._input_text[: self._input_index] + self._input_text[1 + self._input_index:] ) case Keyboard.VK_DELETE: self._update_input( - self._input_text[: self._input_index] + self._input_text[1 + self._input_index :] + self._input_text[: self._input_index] + self._input_text[1 + self._input_index:] ) case Keyboard.VK_CTRL_R: self.reset() @@ -195,13 +202,16 @@ def handle_keypress(self) -> Keyboard: case Keyboard.VK_END: self._input_index = self.length case Keyboard.VK_TAB: - self._update_input(self._input_text + self._tab_complete) + self._update_input(self._input_text + self._suggestion) self._input_index = self.length case _ as key if key.val.isprintable(): + text: str = key.val + while (key := Keyboard.wait_keystroke(False)) != Keyboard.VK_NONE: + if key.val and key.val.isprintable(): + text += key.val self._update_input( - self._input_text[: self._input_index] + key.val + self._input_text[self._input_index :] - ) - self._input_index += 1 + self._input_text[: self._input_index] + text + self._input_text[self._input_index:]) + self._input_index += len(text) case _ as key if key in Keyboard.break_keys(): self._done = True case _: @@ -250,7 +260,7 @@ def _prev_in_history(self) -> str: return text return edt_text - def _write_hint(self) -> None: + def _write_suggestion(self) -> None: """TODO """ edt_text: str = self._input_text filtered: list[str] = list(filter(lambda h: h.startswith(edt_text), self._HISTORY)) @@ -269,4 +279,4 @@ def _write_hint(self) -> None: else: self._terminal.cursor.erase(Direction.DOWN) - self._tab_complete = hint + self._suggestion = hint diff --git a/modules/clitt/src/main/clitt/core/tui/line_input/line_input.py b/modules/clitt/src/main/clitt/core/tui/line_input/line_input.py index 12219baed..64425c3c1 100644 --- a/modules/clitt/src/main/clitt/core/tui/line_input/line_input.py +++ b/modules/clitt/src/main/clitt/core/tui/line_input/line_input.py @@ -19,13 +19,18 @@ def line_input( - prompt: str = "", prompt_color: VtColor = VtColor.NC, text_color: VtColor = VtColor.NC, navbar_enable: bool = False + prompt: str = "", + placeholder: str = "", + prompt_color: VtColor = VtColor.NC, + text_color: VtColor = VtColor.NC, + navbar_enable: bool = False, ) -> Optional[str | Keyboard]: """Read a string from standard input. :param prompt: The message to be displayed to the user. + :param placeholder: The placeholder text. :param prompt_color: The color of the prompt text. :param text_color: The color of the input text. :param navbar_enable: Whether to display the navbar or not. """ - ptt = KeyboardInput(prompt, prompt_color, text_color, navbar_enable) + ptt = KeyboardInput(prompt, placeholder, prompt_color, text_color, navbar_enable) return ptt.execute() diff --git a/modules/clitt/src/main/clitt/core/tui/mchoose/__init__.py b/modules/clitt/src/main/clitt/core/tui/mchoose/__init__.py index 2d0288c64..21dff2763 100644 --- a/modules/clitt/src/main/clitt/core/tui/mchoose/__init__.py +++ b/modules/clitt/src/main/clitt/core/tui/mchoose/__init__.py @@ -1,6 +1,6 @@ # _*_ coding: utf-8 _*_ # -# hspylib-clitt v0.9.122 +# hspylib-clitt v0.9.124 # # Package: main.clitt.core.tui.mchoose """Package initialization.""" @@ -9,4 +9,4 @@ 'mchoose', 'menu_choose' ] -__version__ = '0.9.122' +__version__ = '0.9.124' diff --git a/modules/clitt/src/main/clitt/core/tui/mdashboard/__init__.py b/modules/clitt/src/main/clitt/core/tui/mdashboard/__init__.py index 5d20228e8..72579c1db 100644 --- a/modules/clitt/src/main/clitt/core/tui/mdashboard/__init__.py +++ b/modules/clitt/src/main/clitt/core/tui/mdashboard/__init__.py @@ -1,6 +1,6 @@ # _*_ coding: utf-8 _*_ # -# hspylib-clitt v0.9.122 +# hspylib-clitt v0.9.124 # # Package: main.clitt.core.tui.mdashboard """Package initialization.""" @@ -11,4 +11,4 @@ 'mdashboard', 'menu_dashboard' ] -__version__ = '0.9.122' +__version__ = '0.9.124' diff --git a/modules/clitt/src/main/clitt/core/tui/menu/__init__.py b/modules/clitt/src/main/clitt/core/tui/menu/__init__.py index f2c4f57ed..69e99bb8a 100644 --- a/modules/clitt/src/main/clitt/core/tui/menu/__init__.py +++ b/modules/clitt/src/main/clitt/core/tui/menu/__init__.py @@ -1,6 +1,6 @@ # _*_ coding: utf-8 _*_ # -# hspylib-clitt v0.9.122 +# hspylib-clitt v0.9.124 # # Package: main.clitt.core.tui.menu """Package initialization.""" @@ -13,4 +13,4 @@ 'tui_menu_ui', 'tui_menu_view' ] -__version__ = '0.9.122' +__version__ = '0.9.124' diff --git a/modules/clitt/src/main/clitt/core/tui/minput/__init__.py b/modules/clitt/src/main/clitt/core/tui/minput/__init__.py index bbe338bdd..2d79c5116 100644 --- a/modules/clitt/src/main/clitt/core/tui/minput/__init__.py +++ b/modules/clitt/src/main/clitt/core/tui/minput/__init__.py @@ -1,6 +1,6 @@ # _*_ coding: utf-8 _*_ # -# hspylib-clitt v0.9.122 +# hspylib-clitt v0.9.124 # # Package: main.clitt.core.tui.minput """Package initialization.""" @@ -16,4 +16,4 @@ 'minput', 'minput_utils' ] -__version__ = '0.9.122' +__version__ = '0.9.124' diff --git a/modules/clitt/src/main/clitt/core/tui/minput/input_validator.py b/modules/clitt/src/main/clitt/core/tui/minput/input_validator.py index fc1de6518..47d3ddab3 100644 --- a/modules/clitt/src/main/clitt/core/tui/minput/input_validator.py +++ b/modules/clitt/src/main/clitt/core/tui/minput/input_validator.py @@ -83,7 +83,7 @@ def __call__(self, *args, **kwargs) -> bool: def validate(self, value: str) -> bool: """Validate the value against the validator pattern.""" unmasked_value = re.split("[|,;]", value)[0] if value else "" - return bool(re.match(self.pattern, unmasked_value)) + return all(bool(re.match(self.pattern, v)) for v in unmasked_value) @property def pattern(self) -> str: diff --git a/modules/clitt/src/main/clitt/core/tui/minput/menu_input.py b/modules/clitt/src/main/clitt/core/tui/minput/menu_input.py index 72c60f1d1..c5d770af8 100644 --- a/modules/clitt/src/main/clitt/core/tui/minput/menu_input.py +++ b/modules/clitt/src/main/clitt/core/tui/minput/menu_input.py @@ -173,13 +173,15 @@ def _handle_input(self, keypress: Keyboard) -> None: self._display_error(str(err)) case _: if len(xstr(self.cur_field.value)) < self.cur_field.max_length: - if self.cur_field.validate_input(keypress.value): - self.cur_field.value = (str(self.cur_field.value) if self.cur_field.value else "") + str( - keypress.value - ) + text: str = keypress.val + while (key := Keyboard.wait_keystroke(False)) != Keyboard.VK_NONE: + if key.val and key.val.isprintable(): + text += key.val + if self.cur_field.validate_input(text): + self.cur_field.value = (str(self.cur_field.value) if self.cur_field.value else "") + text else: self._display_error( - f"Input '{keypress.value}' is invalid. " + f"Input '{text}' is not valid. " f"This field takes only {self.cur_field.input_validator}!" ) diff --git a/modules/clitt/src/main/clitt/core/tui/mselect/__init__.py b/modules/clitt/src/main/clitt/core/tui/mselect/__init__.py index e9f4519bc..8008186d9 100644 --- a/modules/clitt/src/main/clitt/core/tui/mselect/__init__.py +++ b/modules/clitt/src/main/clitt/core/tui/mselect/__init__.py @@ -1,6 +1,6 @@ # _*_ coding: utf-8 _*_ # -# hspylib-clitt v0.9.122 +# hspylib-clitt v0.9.124 # # Package: main.clitt.core.tui.mselect """Package initialization.""" @@ -9,4 +9,4 @@ 'menu_select', 'mselect' ] -__version__ = '0.9.122' +__version__ = '0.9.124' diff --git a/modules/clitt/src/main/clitt/core/tui/table/__init__.py b/modules/clitt/src/main/clitt/core/tui/table/__init__.py index ec4f03f13..85cd28393 100644 --- a/modules/clitt/src/main/clitt/core/tui/table/__init__.py +++ b/modules/clitt/src/main/clitt/core/tui/table/__init__.py @@ -1,6 +1,6 @@ # _*_ coding: utf-8 _*_ # -# hspylib-clitt v0.9.122 +# hspylib-clitt v0.9.124 # # Package: main.clitt.core.tui.table """Package initialization.""" @@ -9,4 +9,4 @@ 'table_enums', 'table_renderer' ] -__version__ = '0.9.122' +__version__ = '0.9.124' diff --git a/modules/clitt/src/main/clitt/utils/__init__.py b/modules/clitt/src/main/clitt/utils/__init__.py index a98152b71..4902d86ac 100644 --- a/modules/clitt/src/main/clitt/utils/__init__.py +++ b/modules/clitt/src/main/clitt/utils/__init__.py @@ -1,6 +1,6 @@ # _*_ coding: utf-8 _*_ # -# hspylib-clitt v0.9.122 +# hspylib-clitt v0.9.124 # # Package: main.clitt.utils """Package initialization.""" @@ -8,4 +8,4 @@ __all__ = [ 'git_utils' ] -__version__ = '0.9.122' +__version__ = '0.9.124' diff --git a/modules/clitt/src/test/__init__.py b/modules/clitt/src/test/__init__.py index d721bb6a7..acd36a686 100644 --- a/modules/clitt/src/test/__init__.py +++ b/modules/clitt/src/test/__init__.py @@ -1,6 +1,6 @@ # _*_ coding: utf-8 _*_ # -# hspylib-clitt v0.9.122 +# hspylib-clitt v0.9.124 # # Package: test """Package initialization.""" @@ -8,4 +8,4 @@ __all__ = [ 'test_main' ] -__version__ = '0.9.122' +__version__ = '0.9.124'