mininterface 1.0.0a1__tar.gz → 1.0.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mininterface-1.0.0a1 → mininterface-1.0.2}/PKG-INFO +4 -4
- {mininterface-1.0.0a1 → mininterface-1.0.2}/README.md +3 -3
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/__init__.py +1 -1
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/_mininterface/__init__.py +2 -2
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/_text_interface/adaptor.py +10 -6
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/_tk_interface/__init__.py +5 -5
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/tag/path_tag.py +6 -12
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/tag/select_tag.py +2 -4
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/tag/tag.py +7 -11
- {mininterface-1.0.0a1 → mininterface-1.0.2}/pyproject.toml +1 -1
- {mininterface-1.0.0a1 → mininterface-1.0.2}/LICENSE +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/__main__.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/_lib/__init__.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/_lib/auxiliary.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/_lib/cli_parser.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/_lib/form_dict.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/_lib/redirectable.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/_lib/showcase.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/_lib/start.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/_mininterface/adaptor.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/_mininterface/mixin.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/_text_interface/__init__.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/_text_interface/facet.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/_textual_interface/__init__.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/_textual_interface/adaptor.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/_textual_interface/button_contents.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/_textual_interface/facet.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/_textual_interface/file_picker_input.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/_textual_interface/form_contents.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/_textual_interface/secret_input.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/_textual_interface/style.tcss +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/_textual_interface/textual_app.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/_textual_interface/widgets.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/_tk_interface/adaptor.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/_tk_interface/date_entry.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/_tk_interface/external_fix.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/_tk_interface/facet.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/_tk_interface/redirect_text_tkinter.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/_tk_interface/secret_entry.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/_tk_interface/select_input.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/_tk_interface/utils.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/_web_interface/__init__.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/_web_interface/app.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/_web_interface/child_adaptor.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/_web_interface/parent_adaptor.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/cli.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/exceptions.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/experimental.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/facet/__init__.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/interfaces.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/settings.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/tag/__init__.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/tag/alias.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/tag/callback_tag.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/tag/datetime_tag.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/tag/flag.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/tag/internal.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/tag/secret_tag.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/tag/tag_factory.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/tag/type_stubs.py +0 -0
- {mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/validators.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: mininterface
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.2
|
|
4
4
|
Summary: A minimal access to GUI, TUI, CLI and config
|
|
5
5
|
License: LGPL-3.0-or-later
|
|
6
6
|
Author: Edvard Rejthar
|
|
@@ -165,7 +165,7 @@ There are various bundles. We mark the least permissive licence in the bundle.
|
|
|
165
165
|
Apart from the minimal bundle (which lacks CLI and dataclass support), they have the same functionality, differring only in the user experience.
|
|
166
166
|
|
|
167
167
|
!!! tip
|
|
168
|
-
|
|
168
|
+
For automated testing (e.g., in CI environments), the `mininterface[basic]` bundle is sufficient.
|
|
169
169
|
|
|
170
170
|
## MacOS GUI
|
|
171
171
|
|
|
@@ -188,7 +188,7 @@ Take a look at the following example.
|
|
|
188
188
|
|
|
189
189
|
1. We define any Env class.
|
|
190
190
|
2. Then, we initialize mininterface with [`run(Env)`][mininterface.run] – the missing fields will be prompter for
|
|
191
|
-
3. Then, we use various dialog methods, like [`confirm`][mininterface.Mininterface.confirm], [`
|
|
191
|
+
3. Then, we use various dialog methods, like [`confirm`][mininterface.Mininterface.confirm], [`select`][mininterface.Mininterface.select] or [`form`][mininterface.Mininterface.form].
|
|
192
192
|
|
|
193
193
|
Below, you find the screenshots how the program looks in various environments ([graphic](Interfaces.md#guiinterface-or-tkinterface-or-gui) interface, [web](Interfaces.md#webinterface-or-web) interface...).
|
|
194
194
|
|
|
@@ -216,7 +216,7 @@ if __name__ == "__main__":
|
|
|
216
216
|
# like `confirm` for bool
|
|
217
217
|
if m.confirm("Do you want to continue?"):
|
|
218
218
|
|
|
219
|
-
# or `
|
|
219
|
+
# or `select` for choosing a value
|
|
220
220
|
fruit = m.select(("apple", "banana", "sirup"), "Choose a fruit")
|
|
221
221
|
|
|
222
222
|
if fruit == "apple":
|
|
@@ -127,7 +127,7 @@ There are various bundles. We mark the least permissive licence in the bundle.
|
|
|
127
127
|
Apart from the minimal bundle (which lacks CLI and dataclass support), they have the same functionality, differring only in the user experience.
|
|
128
128
|
|
|
129
129
|
!!! tip
|
|
130
|
-
|
|
130
|
+
For automated testing (e.g., in CI environments), the `mininterface[basic]` bundle is sufficient.
|
|
131
131
|
|
|
132
132
|
## MacOS GUI
|
|
133
133
|
|
|
@@ -150,7 +150,7 @@ Take a look at the following example.
|
|
|
150
150
|
|
|
151
151
|
1. We define any Env class.
|
|
152
152
|
2. Then, we initialize mininterface with [`run(Env)`][mininterface.run] – the missing fields will be prompter for
|
|
153
|
-
3. Then, we use various dialog methods, like [`confirm`][mininterface.Mininterface.confirm], [`
|
|
153
|
+
3. Then, we use various dialog methods, like [`confirm`][mininterface.Mininterface.confirm], [`select`][mininterface.Mininterface.select] or [`form`][mininterface.Mininterface.form].
|
|
154
154
|
|
|
155
155
|
Below, you find the screenshots how the program looks in various environments ([graphic](Interfaces.md#guiinterface-or-tkinterface-or-gui) interface, [web](Interfaces.md#webinterface-or-web) interface...).
|
|
156
156
|
|
|
@@ -178,7 +178,7 @@ if __name__ == "__main__":
|
|
|
178
178
|
# like `confirm` for bool
|
|
179
179
|
if m.confirm("Do you want to continue?"):
|
|
180
180
|
|
|
181
|
-
# or `
|
|
181
|
+
# or `select` for choosing a value
|
|
182
182
|
fruit = m.select(("apple", "banana", "sirup"), "Choose a fruit")
|
|
183
183
|
|
|
184
184
|
if fruit == "apple":
|
|
@@ -37,10 +37,10 @@ class Mininterface(Generic[EnvClass]):
|
|
|
37
37
|
or you can create [one](Interfaces.md) directly (without benefiting from the CLI parsing).
|
|
38
38
|
|
|
39
39
|
Raise:
|
|
40
|
-
[Cancelled][mininterface.exceptions.Cancelled]: A SystemExit based exception noting that the program exits without a traceback, ex. if user hits the escape.
|
|
40
|
+
[Cancelled][mininterface.exceptions.Cancelled]: A `SystemExit` based exception noting that the program exits without a traceback, ex. if user hits the escape.
|
|
41
41
|
|
|
42
42
|
Raise:
|
|
43
|
-
[InterfaceNotAvailable][mininterface.exceptions.InterfaceNotAvailable]: Interface failed to init, ex. display not available in GUI.
|
|
43
|
+
[InterfaceNotAvailable][mininterface.exceptions.InterfaceNotAvailable]: Interface failed to init, ex. display not available in GUI. You don't have to check for it when invoking an interface through safe methods [`run`][mininterface.run] or [`get_interface`][mininterface.interfaces.get_interface].
|
|
44
44
|
"""
|
|
45
45
|
# This base interface does not require any user input and hence is suitable for headless testing.
|
|
46
46
|
|
|
@@ -1,18 +1,22 @@
|
|
|
1
1
|
import warnings
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
try:
|
|
4
|
+
# NOTE does not work in Win, we should find a replacement
|
|
5
|
+
# https://github.com/IngoMeyer441/simple-term-menu/issues/5
|
|
6
|
+
from simple_term_menu import TerminalMenu
|
|
7
|
+
except ImportError:
|
|
8
|
+
from ..exceptions import InterfaceNotAvailable
|
|
9
|
+
raise InterfaceNotAvailable
|
|
6
10
|
|
|
7
11
|
from .._lib.auxiliary import flatten
|
|
8
|
-
from ..exceptions import Cancelled
|
|
9
12
|
from .._lib.form_dict import TagDict
|
|
10
13
|
from .._mininterface import Tag
|
|
11
14
|
from .._mininterface.adaptor import BackendAdaptor
|
|
15
|
+
from ..exceptions import Cancelled
|
|
12
16
|
from ..settings import TextSettings
|
|
13
|
-
from ..tag.internal import
|
|
14
|
-
SubmitButtonWidget)
|
|
17
|
+
from ..tag.internal import BoolWidget, CallbackButtonWidget, SubmitButtonWidget
|
|
15
18
|
from ..tag.secret_tag import SecretTag
|
|
19
|
+
from ..tag.select_tag import SelectTag
|
|
16
20
|
from .facet import TextFacet
|
|
17
21
|
|
|
18
22
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Type
|
|
1
|
+
from typing import Iterable, Type
|
|
2
2
|
|
|
3
3
|
try:
|
|
4
4
|
# It seems tkinter is installed either by default or not installable at all.
|
|
@@ -10,7 +10,7 @@ except ImportError:
|
|
|
10
10
|
|
|
11
11
|
from ..exceptions import InterfaceNotAvailable
|
|
12
12
|
|
|
13
|
-
from .._mininterface import EnvClass, Mininterface, TagValue
|
|
13
|
+
from .._mininterface import EnvClass, Mininterface, TagValue, ValidationCallback
|
|
14
14
|
from .._mininterface.mixin import RichUiMixin
|
|
15
15
|
from ..tag import Tag
|
|
16
16
|
from ..settings import GuiSettings
|
|
@@ -33,8 +33,8 @@ class TkInterface(Redirectable, RichUiMixin, Mininterface):
|
|
|
33
33
|
# The window must disappear completely. Otherwise an empty trailing window would appear in the case another TkInterface would start.
|
|
34
34
|
self._adaptor.destroy()
|
|
35
35
|
|
|
36
|
-
def ask(self, text: str, annotation: Type[TagValue] | Tag = str) -> TagValue:
|
|
37
|
-
if annotation is int:
|
|
36
|
+
def ask(self, text: str, annotation: Type[TagValue] | Tag = str, validation: Iterable[ValidationCallback] | ValidationCallback | None = None) -> TagValue:
|
|
37
|
+
if annotation is int and validation is None:
|
|
38
38
|
# without 0, tkinter_form would create a mere text Entry
|
|
39
39
|
return self.form({text: 0})[text]
|
|
40
|
-
return super().ask(text, annotation)
|
|
40
|
+
return super().ask(text, annotation, validation)
|
|
@@ -82,8 +82,7 @@ class PathTag(Tag[Path | list[Path] | TagValue]):
|
|
|
82
82
|
value = super()._validate(value)
|
|
83
83
|
# Check for multiple paths before any conversion
|
|
84
84
|
if not self.multiple and isinstance(value, (list, tuple)):
|
|
85
|
-
|
|
86
|
-
raise ValueError()
|
|
85
|
+
raise ValueError("Multiple paths are not allowed")
|
|
87
86
|
# Convert to list for validation
|
|
88
87
|
paths = value if isinstance(value, list) else [value]
|
|
89
88
|
|
|
@@ -93,23 +92,18 @@ class PathTag(Tag[Path | list[Path] | TagValue]):
|
|
|
93
92
|
try:
|
|
94
93
|
path = Path(path)
|
|
95
94
|
except Exception:
|
|
96
|
-
|
|
97
|
-
raise ValueError()
|
|
95
|
+
raise ValueError(f"Invalid path format: {path}")
|
|
98
96
|
|
|
99
97
|
if self.exist and not path.exists():
|
|
100
|
-
|
|
101
|
-
raise ValueError()
|
|
98
|
+
raise ValueError(f"Path does not exist: {path}")
|
|
102
99
|
|
|
103
100
|
if self.is_dir and self.is_file:
|
|
104
|
-
|
|
105
|
-
raise ValueError()
|
|
101
|
+
raise ValueError(f"Path cannot be both a file and a directory: {path}")
|
|
106
102
|
|
|
107
103
|
if self.is_dir and not path.is_dir():
|
|
108
|
-
|
|
109
|
-
raise ValueError()
|
|
104
|
+
raise ValueError(f"Path is not a directory: {path}")
|
|
110
105
|
|
|
111
106
|
if self.is_file and not path.is_file():
|
|
112
|
-
|
|
113
|
-
raise ValueError()
|
|
107
|
+
raise ValueError(f"Path is not a file: {path}")
|
|
114
108
|
|
|
115
109
|
return value
|
|
@@ -312,11 +312,9 @@ class SelectTag(Tag[TagValue]):
|
|
|
312
312
|
if all(v in vals for v in out_value):
|
|
313
313
|
return out_value
|
|
314
314
|
else:
|
|
315
|
-
|
|
316
|
-
raise ValueError
|
|
315
|
+
raise ValueError(f"A value is not one of the options")
|
|
317
316
|
else:
|
|
318
317
|
if out_value in vals:
|
|
319
318
|
return out_value
|
|
320
319
|
else:
|
|
321
|
-
|
|
322
|
-
raise ValueError
|
|
320
|
+
raise ValueError(f"Not one of the allowed values")
|
|
@@ -749,8 +749,7 @@ class Tag(Generic[TagValue]):
|
|
|
749
749
|
# Ex. putting "2.0" into an int.
|
|
750
750
|
# It would generate type problem later here in the method,
|
|
751
751
|
# but even now comparison failed Ex. TypeError("2.0" > 0)
|
|
752
|
-
|
|
753
|
-
raise ValueError
|
|
752
|
+
raise ValueError(f"Type must be {self._repr_annotation()}!")
|
|
754
753
|
else:
|
|
755
754
|
res = vald(self)
|
|
756
755
|
if isinstance(res, tuple):
|
|
@@ -760,16 +759,14 @@ class Tag(Generic[TagValue]):
|
|
|
760
759
|
passed = res
|
|
761
760
|
self.val = last
|
|
762
761
|
if passed is not True: # we did not pass, there might be an error message in passed
|
|
763
|
-
|
|
764
|
-
raise ValueError
|
|
762
|
+
raise ValueError(passed or f"Validation fail")
|
|
765
763
|
|
|
766
764
|
# pydantic_check
|
|
767
765
|
if self._pydantic_field:
|
|
768
766
|
try:
|
|
769
767
|
create_model('ValidationModel', check=(self.annotation, self._pydantic_field))(check=out_value)
|
|
770
768
|
except PydanticValidationError as e:
|
|
771
|
-
|
|
772
|
-
raise ValueError
|
|
769
|
+
raise ValueError(e.errors()[0]["msg"])
|
|
773
770
|
# attrs check
|
|
774
771
|
if self._attrs_field:
|
|
775
772
|
try:
|
|
@@ -778,13 +775,11 @@ class Tag(Generic[TagValue]):
|
|
|
778
775
|
{"check": attr.ib(validator=self._attrs_field.validator)}
|
|
779
776
|
)(check=out_value)
|
|
780
777
|
except ValueError as e:
|
|
781
|
-
|
|
782
|
-
raise
|
|
778
|
+
raise ValueError(str(e))
|
|
783
779
|
|
|
784
780
|
# Type check
|
|
785
781
|
if not self._is_right_instance(out_value):
|
|
786
|
-
|
|
787
|
-
raise ValueError
|
|
782
|
+
raise ValueError(f"Type must be {self._repr_annotation()}!")
|
|
788
783
|
|
|
789
784
|
return out_value
|
|
790
785
|
|
|
@@ -870,7 +865,8 @@ class Tag(Generic[TagValue]):
|
|
|
870
865
|
# User and type validation check
|
|
871
866
|
try:
|
|
872
867
|
self.val = self._validate(out_value) # checks succeeded, confirm the value
|
|
873
|
-
except ValueError:
|
|
868
|
+
except ValueError as e:
|
|
869
|
+
self.set_error_text(str(e))
|
|
874
870
|
return False
|
|
875
871
|
self._update_source(out_value)
|
|
876
872
|
return True
|
|
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
|
|
|
4
4
|
|
|
5
5
|
[tool.poetry]
|
|
6
6
|
name = "mininterface"
|
|
7
|
-
version = "1.0.
|
|
7
|
+
version = "1.0.2"
|
|
8
8
|
description = "A minimal access to GUI, TUI, CLI and config"
|
|
9
9
|
authors = ["Edvard Rejthar <edvard.rejthar@nic.cz>"]
|
|
10
10
|
license = "LGPL-3.0-or-later"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/_textual_interface/button_contents.py
RENAMED
|
File without changes
|
|
File without changes
|
{mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/_textual_interface/file_picker_input.py
RENAMED
|
File without changes
|
{mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/_textual_interface/form_contents.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{mininterface-1.0.0a1 → mininterface-1.0.2}/mininterface/_tk_interface/redirect_text_tkinter.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|