mininterface 0.6.2rc2__tar.gz → 0.7.1__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.
Files changed (46) hide show
  1. {mininterface-0.6.2rc2 → mininterface-0.7.1}/PKG-INFO +51 -30
  2. {mininterface-0.6.2rc2 → mininterface-0.7.1}/README.md +41 -26
  3. mininterface-0.7.1/mininterface/ValidationFail.py +5 -0
  4. {mininterface-0.6.2rc2 → mininterface-0.7.1}/mininterface/__init__.py +62 -49
  5. mininterface-0.7.1/mininterface/__main__.py +82 -0
  6. mininterface-0.7.1/mininterface/auxiliary.py +151 -0
  7. mininterface-0.7.1/mininterface/cli_parser.py +272 -0
  8. mininterface-0.7.1/mininterface/exceptions.py +30 -0
  9. {mininterface-0.6.2rc2 → mininterface-0.7.1}/mininterface/facet.py +62 -8
  10. {mininterface-0.6.2rc2 → mininterface-0.7.1}/mininterface/form_dict.py +73 -32
  11. mininterface-0.7.1/mininterface/interfaces.py +58 -0
  12. {mininterface-0.6.2rc2 → mininterface-0.7.1}/mininterface/mininterface.py +132 -38
  13. {mininterface-0.6.2rc2 → mininterface-0.7.1}/mininterface/redirectable.py +9 -1
  14. mininterface-0.7.1/mininterface/showcase.py +82 -0
  15. mininterface-0.7.1/mininterface/start.py +95 -0
  16. mininterface-0.7.1/mininterface/subcommands.py +141 -0
  17. {mininterface-0.6.2rc2 → mininterface-0.7.1}/mininterface/tag.py +148 -56
  18. mininterface-0.7.1/mininterface/tag_factory.py +80 -0
  19. {mininterface-0.6.2rc2 → mininterface-0.7.1}/mininterface/text_interface.py +61 -7
  20. mininterface-0.7.1/mininterface/textual_interface/__init__.py +51 -0
  21. mininterface-0.7.1/mininterface/textual_interface/textual_adaptor.py +95 -0
  22. mininterface-0.7.1/mininterface/textual_interface/textual_app.py +105 -0
  23. {mininterface-0.6.2rc2 → mininterface-0.7.1}/mininterface/textual_interface/textual_button_app.py +3 -3
  24. mininterface-0.7.1/mininterface/textual_interface/textual_facet.py +31 -0
  25. {mininterface-0.6.2rc2 → mininterface-0.7.1}/mininterface/textual_interface/widgets.py +2 -6
  26. {mininterface-0.6.2rc2 → mininterface-0.7.1}/mininterface/tk_interface/__init__.py +17 -11
  27. mininterface-0.7.1/mininterface/tk_interface/date_entry.py +221 -0
  28. {mininterface-0.6.2rc2 → mininterface-0.7.1}/mininterface/tk_interface/redirect_text_tkinter.py +9 -0
  29. mininterface-0.7.1/mininterface/tk_interface/tk_facet.py +55 -0
  30. {mininterface-0.6.2rc2 → mininterface-0.7.1}/mininterface/tk_interface/tk_window.py +49 -22
  31. {mininterface-0.6.2rc2 → mininterface-0.7.1}/mininterface/tk_interface/utils.py +19 -13
  32. {mininterface-0.6.2rc2 → mininterface-0.7.1}/mininterface/type_stubs.py +3 -2
  33. {mininterface-0.6.2rc2 → mininterface-0.7.1}/mininterface/types.py +91 -9
  34. {mininterface-0.6.2rc2 → mininterface-0.7.1}/mininterface/validators.py +1 -1
  35. {mininterface-0.6.2rc2 → mininterface-0.7.1}/pyproject.toml +15 -5
  36. mininterface-0.6.2rc2/mininterface/__main__.py +0 -39
  37. mininterface-0.6.2rc2/mininterface/auxiliary.py +0 -61
  38. mininterface-0.6.2rc2/mininterface/cli_parser.py +0 -210
  39. mininterface-0.6.2rc2/mininterface/common.py +0 -8
  40. mininterface-0.6.2rc2/mininterface/tag_factory.py +0 -28
  41. mininterface-0.6.2rc2/mininterface/textual_interface/__init__.py +0 -59
  42. mininterface-0.6.2rc2/mininterface/textual_interface/textual_app.py +0 -147
  43. mininterface-0.6.2rc2/mininterface/textual_interface/textual_facet.py +0 -21
  44. mininterface-0.6.2rc2/mininterface/tk_interface/tk_facet.py +0 -20
  45. {mininterface-0.6.2rc2 → mininterface-0.7.1}/LICENSE +0 -0
  46. {mininterface-0.6.2rc2 → mininterface-0.7.1}/mininterface/experimental.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mininterface
3
- Version: 0.6.2rc2
3
+ Version: 0.7.1
4
4
  Summary: A minimal access to GUI, TUI, CLI and config
5
5
  Home-page: https://github.com/CZ-NIC/mininterface
6
6
  License: GPL-3.0-or-later
@@ -13,24 +13,30 @@ Classifier: Programming Language :: Python :: 3.10
13
13
  Classifier: Programming Language :: Python :: 3.11
14
14
  Classifier: Programming Language :: Python :: 3.12
15
15
  Classifier: Programming Language :: Python :: 3.13
16
+ Provides-Extra: all
17
+ Provides-Extra: img
18
+ Provides-Extra: web
16
19
  Requires-Dist: autocombobox (==1.4.2)
20
+ Requires-Dist: humanize
17
21
  Requires-Dist: pyyaml
18
- Requires-Dist: requests
19
- Requires-Dist: textual
22
+ Requires-Dist: textual (>=0.84,<0.85)
23
+ Requires-Dist: tkcalendar
20
24
  Requires-Dist: tkinter-tooltip
21
25
  Requires-Dist: tkinter_form (==0.1.5.2)
22
- Requires-Dist: tyro
26
+ Requires-Dist: tkscrollableframe
27
+ Requires-Dist: typing_extensions
28
+ Requires-Dist: tyro (==0.8.14)
23
29
  Description-Content-Type: text/markdown
24
30
 
25
31
  # Mininterface – access to GUI, TUI, CLI and config files
26
32
  [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
27
33
  [![Build Status](https://github.com/CZ-NIC/mininterface/actions/workflows/run-unittest.yml/badge.svg)](https://github.com/CZ-NIC/mininterface/actions)
28
- [![Downloads](https://pepy.tech/badge/mininterface)](https://pepy.tech/project/mininterface)
34
+ [![Downloads](https://static.pepy.tech/badge/mininterface)](https://pepy.tech/project/mininterface)
29
35
 
30
36
  Write the program core, do not bother with the input/output.
31
37
 
32
- ![Hello world example: GUI window](https://github.com/CZ-NIC/mininterface/blob/main/asset/hello-world.png?raw=True "A minimal use case – GUI")
33
- ![Hello world example: TUI fallback](https://github.com/CZ-NIC/mininterface/blob/main/asset/hello-tui.webp?raw=True "A minimal use case – TUI fallback")
38
+ ![Hello world example: GUI window](https://github.com/CZ-NIC/mininterface/blob/main/asset/hello-gui.avif?raw=True "A minimal use case – GUI")
39
+ ![Hello world example: TUI fallback](https://github.com/CZ-NIC/mininterface/blob/main/asset/hello-tui.avif?raw=True "A minimal use case – TUI fallback")
34
40
 
35
41
  Check out the code, which is surprisingly short, that displays such a window or its textual fallback.
36
42
 
@@ -49,10 +55,11 @@ class Env:
49
55
  """ This number is very important """
50
56
 
51
57
  if __name__ == "__main__":
52
- env = run(Env, prog="My application").env
58
+ m = run(Env, prog="My application")
59
+ m.form()
53
60
  # Attributes are suggested by the IDE
54
61
  # along with the hint text 'This number is very important'.
55
- print(env.my_number)
62
+ print(m.env.my_number)
56
63
  ```
57
64
 
58
65
  # Contents
@@ -69,16 +76,18 @@ It was all the code you need. No lengthy blocks of code imposed by an external d
69
76
 
70
77
 
71
78
  ```bash
72
- $ ./hello.py
73
- usage: My application [-h] [--test | --no-test] [--important-number INT]
79
+ $ ./program.py --help
80
+ usage: My application [-h] [-v] [--my-flag | --no-my-flag] [--my-number INT]
74
81
 
75
82
  This calculates something.
76
83
 
77
- ╭─ options ──────────────────────────────────────────────────────────╮
78
- │ -h, --help show this help message and exit
79
- --test, --no-test My testing flag (default: False)
80
- │ --important-number INT This number is very important (default: 4)
81
- ╰────────────────────────────────────────────────────────────────────╯
84
+ ╭─ options ───────────────────────────────────────────────────────────────╮
85
+ │ -h, --help show this help message and exit
86
+ -v, --verbose Verbosity level. Can be used twice to increase.
87
+ │ --my-flag, --no-my-flag
88
+ │ This switches the functionality (default: False) │
89
+ │ --my-number INT This number is very important (default: 4) │
90
+ ╰─────────────────────────────────────────────────────────────────────────╯
82
91
  ```
83
92
 
84
93
  ## You got config file management
@@ -88,6 +97,12 @@ Loading config file is a piece of cake. Alongside `program.py`, put `program.yam
88
97
  my_number: 555
89
98
  ```
90
99
 
100
+ ```bash
101
+ $ program.py --help
102
+ ...
103
+ │ --my-number INT This number is very important (default: 555) │
104
+ ```
105
+
91
106
  ## You got dialogues
92
107
  Check out several useful methods to handle user dialogues. Here we bound the interface to a `with` statement that redirects stdout directly to the window.
93
108
 
@@ -120,11 +135,22 @@ Install with a single command from [PyPi](https://pypi.org/project/mininterface/
120
135
  pip install mininterface
121
136
  ```
122
137
 
138
+ ## Minimal installation
139
+
140
+ Should you need just the CLI part and you are happy with basic text dialogs, use these commands instead:
141
+
142
+ ```bash
143
+ pip install --no-dependencies mininterface
144
+ pip install tyro typing_extensions pyyaml
145
+ ```
146
+
123
147
  # Docs
124
148
  See the docs overview at [https://cz-nic.github.io/mininterface/](https://cz-nic.github.io/mininterface/Overview/).
125
149
 
126
150
  # Examples
127
151
 
152
+ A powerful [`m.form`](https://cz-nic.github.io/mininterface/Mininterface/#mininterface.Mininterface.form) dialog method accepts either a dataclass or a dict. Take a look on both.
153
+
128
154
  ## A complex dataclass.
129
155
 
130
156
  ```python3
@@ -143,7 +169,7 @@ class Env:
143
169
  nested_config: NestedEnv
144
170
 
145
171
  mandatory_str: str
146
- """ As there is not default value, you will be prompted automatically to fill up the field """
172
+ """ As there is no default value, you will be prompted automatically to fill up the field """
147
173
 
148
174
  my_number: int | None = None
149
175
  """ This is not just a dummy number, if left empty, it is None. """
@@ -167,6 +193,14 @@ print(m.env)
167
193
  m.form()
168
194
  ```
169
195
 
196
+ As the attribute `mandatory_str` requires a value, a prompt appears automatically:
197
+
198
+ ![Complex example missing field](https://github.com/CZ-NIC/mininterface/blob/main/asset/complex_example_missing_field.avif?raw=True)
199
+
200
+ Then, full form appears:
201
+
202
+ ![Complex example](https://github.com/CZ-NIC/mininterface/blob/main/asset/complex_example.avif?raw=True)
203
+
170
204
  ## Form with paths
171
205
 
172
206
  We have a dict with some paths. Here is how it looks.
@@ -187,16 +221,3 @@ m.form(my_dictionary)
187
221
 
188
222
  ![List of paths](https://github.com/CZ-NIC/mininterface/blob/main/asset/list_of_paths.avif?raw=True)
189
223
 
190
-
191
-
192
-
193
-
194
-
195
-
196
-
197
-
198
-
199
-
200
-
201
-
202
-
@@ -1,12 +1,12 @@
1
1
  # Mininterface – access to GUI, TUI, CLI and config files
2
2
  [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
3
3
  [![Build Status](https://github.com/CZ-NIC/mininterface/actions/workflows/run-unittest.yml/badge.svg)](https://github.com/CZ-NIC/mininterface/actions)
4
- [![Downloads](https://pepy.tech/badge/mininterface)](https://pepy.tech/project/mininterface)
4
+ [![Downloads](https://static.pepy.tech/badge/mininterface)](https://pepy.tech/project/mininterface)
5
5
 
6
6
  Write the program core, do not bother with the input/output.
7
7
 
8
- ![Hello world example: GUI window](https://github.com/CZ-NIC/mininterface/blob/main/asset/hello-world.png?raw=True "A minimal use case – GUI")
9
- ![Hello world example: TUI fallback](https://github.com/CZ-NIC/mininterface/blob/main/asset/hello-tui.webp?raw=True "A minimal use case – TUI fallback")
8
+ ![Hello world example: GUI window](https://github.com/CZ-NIC/mininterface/blob/main/asset/hello-gui.avif?raw=True "A minimal use case – GUI")
9
+ ![Hello world example: TUI fallback](https://github.com/CZ-NIC/mininterface/blob/main/asset/hello-tui.avif?raw=True "A minimal use case – TUI fallback")
10
10
 
11
11
  Check out the code, which is surprisingly short, that displays such a window or its textual fallback.
12
12
 
@@ -25,10 +25,11 @@ class Env:
25
25
  """ This number is very important """
26
26
 
27
27
  if __name__ == "__main__":
28
- env = run(Env, prog="My application").env
28
+ m = run(Env, prog="My application")
29
+ m.form()
29
30
  # Attributes are suggested by the IDE
30
31
  # along with the hint text 'This number is very important'.
31
- print(env.my_number)
32
+ print(m.env.my_number)
32
33
  ```
33
34
 
34
35
  # Contents
@@ -45,16 +46,18 @@ It was all the code you need. No lengthy blocks of code imposed by an external d
45
46
 
46
47
 
47
48
  ```bash
48
- $ ./hello.py
49
- usage: My application [-h] [--test | --no-test] [--important-number INT]
49
+ $ ./program.py --help
50
+ usage: My application [-h] [-v] [--my-flag | --no-my-flag] [--my-number INT]
50
51
 
51
52
  This calculates something.
52
53
 
53
- ╭─ options ──────────────────────────────────────────────────────────╮
54
- │ -h, --help show this help message and exit
55
- --test, --no-test My testing flag (default: False)
56
- │ --important-number INT This number is very important (default: 4)
57
- ╰────────────────────────────────────────────────────────────────────╯
54
+ ╭─ options ───────────────────────────────────────────────────────────────╮
55
+ │ -h, --help show this help message and exit
56
+ -v, --verbose Verbosity level. Can be used twice to increase.
57
+ │ --my-flag, --no-my-flag
58
+ │ This switches the functionality (default: False) │
59
+ │ --my-number INT This number is very important (default: 4) │
60
+ ╰─────────────────────────────────────────────────────────────────────────╯
58
61
  ```
59
62
 
60
63
  ## You got config file management
@@ -64,6 +67,12 @@ Loading config file is a piece of cake. Alongside `program.py`, put `program.yam
64
67
  my_number: 555
65
68
  ```
66
69
 
70
+ ```bash
71
+ $ program.py --help
72
+ ...
73
+ │ --my-number INT This number is very important (default: 555) │
74
+ ```
75
+
67
76
  ## You got dialogues
68
77
  Check out several useful methods to handle user dialogues. Here we bound the interface to a `with` statement that redirects stdout directly to the window.
69
78
 
@@ -96,11 +105,22 @@ Install with a single command from [PyPi](https://pypi.org/project/mininterface/
96
105
  pip install mininterface
97
106
  ```
98
107
 
108
+ ## Minimal installation
109
+
110
+ Should you need just the CLI part and you are happy with basic text dialogs, use these commands instead:
111
+
112
+ ```bash
113
+ pip install --no-dependencies mininterface
114
+ pip install tyro typing_extensions pyyaml
115
+ ```
116
+
99
117
  # Docs
100
118
  See the docs overview at [https://cz-nic.github.io/mininterface/](https://cz-nic.github.io/mininterface/Overview/).
101
119
 
102
120
  # Examples
103
121
 
122
+ A powerful [`m.form`](https://cz-nic.github.io/mininterface/Mininterface/#mininterface.Mininterface.form) dialog method accepts either a dataclass or a dict. Take a look on both.
123
+
104
124
  ## A complex dataclass.
105
125
 
106
126
  ```python3
@@ -119,7 +139,7 @@ class Env:
119
139
  nested_config: NestedEnv
120
140
 
121
141
  mandatory_str: str
122
- """ As there is not default value, you will be prompted automatically to fill up the field """
142
+ """ As there is no default value, you will be prompted automatically to fill up the field """
123
143
 
124
144
  my_number: int | None = None
125
145
  """ This is not just a dummy number, if left empty, it is None. """
@@ -143,6 +163,14 @@ print(m.env)
143
163
  m.form()
144
164
  ```
145
165
 
166
+ As the attribute `mandatory_str` requires a value, a prompt appears automatically:
167
+
168
+ ![Complex example missing field](https://github.com/CZ-NIC/mininterface/blob/main/asset/complex_example_missing_field.avif?raw=True)
169
+
170
+ Then, full form appears:
171
+
172
+ ![Complex example](https://github.com/CZ-NIC/mininterface/blob/main/asset/complex_example.avif?raw=True)
173
+
146
174
  ## Form with paths
147
175
 
148
176
  We have a dict with some paths. Here is how it looks.
@@ -162,16 +190,3 @@ m.form(my_dictionary)
162
190
  ```
163
191
 
164
192
  ![List of paths](https://github.com/CZ-NIC/mininterface/blob/main/asset/list_of_paths.avif?raw=True)
165
-
166
-
167
-
168
-
169
-
170
-
171
-
172
-
173
-
174
-
175
-
176
-
177
-
@@ -0,0 +1,5 @@
1
+ class ValidationFail(ValueError):
2
+ """ Signal to the form that submit failed and we want to restore it.
3
+ """
4
+ # NOTE example
5
+ pass
@@ -1,31 +1,20 @@
1
1
  import sys
2
+ from dataclasses import dataclass
2
3
  from pathlib import Path
3
- from typing import TYPE_CHECKING, Type
4
+ from typing import Literal, Optional, Sequence, Type
4
5
 
5
- from .types import Validation, Choices, PathTag
6
- from .cli_parser import _parse_cli
7
- from .common import InterfaceNotAvailable, Cancelled
6
+ from .exceptions import Cancelled, InterfaceNotAvailable
7
+
8
+ from .interfaces import get_interface
9
+
10
+ from . import validators
11
+ from .cli_parser import _parse_cli, assure_args
12
+ from .subcommands import Command, SubcommandPlaceholder
8
13
  from .form_dict import DataClass, EnvClass
9
- from .tag import Tag
10
14
  from .mininterface import EnvClass, Mininterface
11
- from .text_interface import ReplInterface, TextInterface
12
- from . import validators
13
-
14
- # Import optional interfaces
15
- try:
16
- from mininterface.tk_interface import TkInterface
17
- except ImportError:
18
- if TYPE_CHECKING:
19
- pass # Replace TYPE_CHECKING with `type GuiInterface = None` since Python 3.12
20
- else:
21
- TkInterface = None
22
- try:
23
- from mininterface.textual_interface import TextualInterface
24
- except ImportError:
25
- TextualInterface = None
26
-
27
- GuiInterface = TkInterface
28
- TuiInterface = TextualInterface or TextInterface
15
+ from .start import Start
16
+ from .tag import Tag
17
+ from .types import Choices, PathTag, Validation
29
18
 
30
19
  # NOTE:
31
20
  # ask_for_missing does not work with tyro Positional, stays missing.
@@ -33,14 +22,22 @@ TuiInterface = TextualInterface or TextInterface
33
22
  # class Env:
34
23
  # files: Positional[list[Path]]
35
24
 
25
+ # NOTE: imgs missing in Interfaces.md
26
+
27
+
28
+ @dataclass
29
+ class _Empty:
30
+ pass
31
+
36
32
 
37
- def run(env_class: Type[EnvClass] | None = None,
33
+ def run(env_or_list: Type[EnvClass] | list[Type[Command]] | None = None,
38
34
  ask_on_empty_cli: bool = False,
39
35
  title: str = "",
40
36
  config_file: Path | str | bool = True,
41
37
  add_verbosity: bool = True,
42
38
  ask_for_missing: bool = True,
43
- interface: Type[Mininterface] = GuiInterface or TuiInterface,
39
+ interface: Type[Mininterface] | Literal["gui"] | Literal["tui"] | None = None,
40
+ args: Optional[Sequence[str]] = None,
44
41
  **kwargs) -> Mininterface[EnvClass]:
45
42
  """ The main access, start here.
46
43
  Wrap your configuration dataclass into `run` to access the interface. An interface is chosen automatically,
@@ -51,7 +48,12 @@ def run(env_class: Type[EnvClass] | None = None,
51
48
  with the program name ending on *.yaml*, ex: `program.py` will fetch `./program.yaml`.
52
49
 
53
50
  Args:
54
- env_class: Dataclass with the configuration. Their values will be modified with the CLI arguments.
51
+ env_or_list:
52
+ * `dataclass` Dataclass with the configuration. Their values will be modified with the CLI arguments.
53
+ * `list` of [Commands][mininterface.subcommands.Command] let you create multiple commands within a single program, each with unique options.
54
+ * `None` You need just the dialogs, no CLI/config file parsing.
55
+
56
+
55
57
  ask_on_empty_cli: If program was launched with no arguments (empty CLI), invokes self.form() to edit the fields.
56
58
  (Withdrawn when `ask_for_missing` happens.)
57
59
  ```python
@@ -106,8 +108,10 @@ def run(env_class: Type[EnvClass] | None = None,
106
108
  $ program.py # omitting --required-number
107
109
  # Dialog for `required_number` appears
108
110
  ```
109
- interface: Which interface to prefer. By default, we use the GUI, the fallback is the TUI. See the full [list](Overview.md#all-possible-interfaces) of possible interfaces.
110
-
111
+ interface: Which interface to prefer. By default, we use the GUI, the fallback is the TUI.
112
+ You may write "gui" or "tui" literal or pass a specific Mininterface type,
113
+ see the full [list](Interfaces.md) of possible interfaces.
114
+ args: Parse arguments from a sequence instead of the command line.
111
115
  Kwargs:
112
116
  The same as for [argparse.ArgumentParser](https://docs.python.org/3/library/argparse.html).
113
117
 
@@ -119,8 +123,15 @@ def run(env_class: Type[EnvClass] | None = None,
119
123
  The stdout will be redirected to the interface (ex. a GUI window).
120
124
 
121
125
  ```python
126
+ from dataclasses import dataclass
127
+ from mininterface import run
128
+
129
+ @dataclass
130
+ class Env:
131
+ my_number: int = 4
132
+
122
133
  with run(Env) as m:
123
- print(f"Your important number is {m.env.important_number}")
134
+ print(f"Your important number is {m.env.my_number}")
124
135
  boolean = m.is_yes("Is that alright?")
125
136
  ```
126
137
 
@@ -139,7 +150,7 @@ def run(env_class: Type[EnvClass] | None = None,
139
150
  # Undocumented experimental: `default` keyword argument for tyro may serve for default values instead of a config file.
140
151
 
141
152
  # Prepare the config file
142
- if config_file is True and not kwargs.get("default") and env_class:
153
+ if config_file is True and not kwargs.get("default") and env_or_list:
143
154
  # Undocumented feature. User put a namespace into kwargs["default"]
144
155
  # that already serves for defaults. We do not fetch defaults yet from a config file.
145
156
  try:
@@ -155,26 +166,29 @@ def run(env_class: Type[EnvClass] | None = None,
155
166
  elif isinstance(config_file, str):
156
167
  config_file = Path(config_file)
157
168
 
158
- # Load configuration from CLI and a config file
169
+ # Determine title
170
+ title = title or kwargs.get("prog") or Path(sys.argv[0]).name
171
+ start = Start(title, interface)
172
+
173
+ # Hidden meta-commands in args
174
+ args = assure_args(args)
175
+ if len(args) == 1 and args[0] == "--integrate-to-system":
176
+ start.integrate(env_or_list or _Empty)
177
+ quit()
178
+
159
179
  env, wrong_fields = None, {}
160
- if env_class:
161
- verb_ = add_verbosity and "verbose" not in env_class.__annotations__
162
- env, wrong_fields = _parse_cli(env_class, config_file, verb_, ask_for_missing, **kwargs)
180
+ if isinstance(env_or_list, list) and SubcommandPlaceholder in env_or_list and args and args[0] == "subcommand":
181
+ start.choose_subcommand(env_or_list, args=args[1:])
182
+ elif isinstance(env_or_list, list) and not args:
183
+ start.choose_subcommand(env_or_list)
184
+ elif env_or_list:
185
+ # Load configuration from CLI and a config file
186
+ env, wrong_fields = _parse_cli(env_or_list, config_file, add_verbosity, ask_for_missing, args, **kwargs)
187
+ else: # even though there is no configuration, yet we need to parse CLI for meta-commands like --help or --verbose
188
+ _parse_cli(_Empty, None, add_verbosity, ask_for_missing, args)
163
189
 
164
190
  # Build the interface
165
- title = title or kwargs.get("prog") or Path(sys.argv[0]).name
166
- if "prog" not in kwargs:
167
- kwargs["prog"] = title
168
- try:
169
- if interface == "tui": # undocumented feature
170
- interface = TuiInterface
171
- elif interface == "gui": # undocumented feature
172
- interface = GuiInterface
173
- if interface is None:
174
- raise InterfaceNotAvailable # GuiInterface might be None when import fails
175
- interface = interface(title, env)
176
- except InterfaceNotAvailable: # Fallback to a different interface
177
- interface = TuiInterface(title, env)
191
+ interface = get_interface(title, interface, env)
178
192
 
179
193
  # Empty CLI → GUI edit
180
194
  if ask_for_missing and wrong_fields:
@@ -189,5 +203,4 @@ def run(env_class: Type[EnvClass] | None = None,
189
203
 
190
204
  __all__ = ["run", "Tag", "validators", "InterfaceNotAvailable", "Cancelled",
191
205
  "Validation", "Choices", "PathTag",
192
- "Mininterface", "GuiInterface", "TuiInterface", "TextInterface", "TextualInterface", "TkInterface"
193
- ]
206
+ "Mininterface"]
@@ -0,0 +1,82 @@
1
+ from dataclasses import dataclass
2
+ import sys
3
+ from typing import Literal, Optional
4
+ from tyro.conf import FlagConversionOff
5
+
6
+ from .exceptions import DependencyRequired
7
+
8
+ from . import run, Mininterface
9
+ from .showcase import showcase
10
+
11
+ __doc__ = """Simple GUI dialog. Outputs the value the user entered."""
12
+
13
+
14
+ @dataclass
15
+ class Web:
16
+ """ Experimenal undocumented feature. """
17
+
18
+ cmd: str = ""
19
+ """ Launch a miniterface program, while the TextualInterface will be exposed to the web."""
20
+ # NOTE: The textual app ends after the first submit. We have to correct that before the web makes sense.
21
+ # with run(interface=TextualInterface) as m:
22
+ # m.form({"hello": 1}) # the app ends here
23
+ # m.form({"hello": 2}) # we never get here
24
+
25
+ port: int = 64646
26
+
27
+
28
+ InterfaceType = Literal["gui"] | Literal["tui"] | Literal["all"]
29
+ Showcase = Literal[1] | Literal[2]
30
+
31
+
32
+ @dataclass
33
+ class CliInteface:
34
+ web: Web
35
+ alert: str = ""
36
+ """ Display the OK dialog with text. """
37
+ ask: str = ""
38
+ """ Prompt the user to input a text. """
39
+ ask_number: str = ""
40
+ """ Prompt the user to input a number. Empty input = 0. """
41
+ is_yes: str = ""
42
+ """ Display confirm box, focusing 'yes'. """
43
+ is_no: str = ""
44
+ """ Display confirm box, focusing 'no'. """
45
+
46
+ showcase: Optional[tuple[InterfaceType, Showcase]] = None
47
+ """ Prints various form just to show what's possible."""
48
+
49
+
50
+ def web(m: Mininterface):
51
+ try:
52
+ from textual_serve.server import Server
53
+ except ImportError:
54
+ raise DependencyRequired("web")
55
+ server = Server(m.env.web.cmd, port=m.env.web.port)
56
+ server.serve()
57
+
58
+
59
+ def main():
60
+ result = []
61
+ # We tested both GuiInterface and TextualInterface are able to pass a variable to i.e. a bash script.
62
+ # NOTE TextInterface fails (`mininterface --ask Test | grep Hello` – pipe causes no visible output).
63
+ with run(CliInteface, prog="Mininterface", description=__doc__) as m:
64
+ for method, label in vars(m.env).items():
65
+ if method in ["web", "showcase"]: # processed later
66
+ continue
67
+ if label:
68
+ result.append(getattr(m, method)(label))
69
+
70
+ # Displays each result on a new line. Currently, this is an undocumented feature.
71
+ # As we use the script for a single value only and it is not currently possible
72
+ # to ask two numbers or determine a dialog order etc.
73
+ [print(val) for val in result]
74
+
75
+ if m.env.web.cmd:
76
+ web(m)
77
+ if m.env.showcase:
78
+ showcase(*m.env.showcase)
79
+
80
+
81
+ if __name__ == "__main__":
82
+ main()