mininterface 1.2.1__tar.gz → 1.2.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.2.1 → mininterface-1.2.2}/PKG-INFO +5 -5
- {mininterface-1.2.1 → mininterface-1.2.2}/README.md +4 -4
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_lib/auxiliary.py +81 -35
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_lib/cli_parser.py +1 -9
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_lib/dataclass_creation.py +12 -5
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_lib/tyro_patches.py +22 -9
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_tk_interface/adaptor.py +21 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_tk_interface/secret_entry.py +1 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_tk_interface/select_input.py +12 -1
- {mininterface-1.2.1 → mininterface-1.2.2}/pyproject.toml +1 -1
- {mininterface-1.2.1 → mininterface-1.2.2}/LICENSE +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/__init__.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/__main__.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_lib/__init__.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_lib/argparse_support.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_lib/cli_flags.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_lib/cli_utils.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_lib/config_file.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_lib/form_dict.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_lib/future_compatibility.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_lib/redirectable.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_lib/run.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_lib/shortcuts.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_lib/showcase.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_lib/start.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_mininterface/__init__.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_mininterface/adaptor.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_mininterface/mixin.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_text_interface/__init__.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_text_interface/adaptor.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_text_interface/facet.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_text_interface/timeout.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_textual_interface/__init__.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_textual_interface/adaptor.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_textual_interface/button_contents.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_textual_interface/facet.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_textual_interface/file_picker_input.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_textual_interface/form_contents.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_textual_interface/secret_input.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_textual_interface/style.tcss +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_textual_interface/textual_app.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_textual_interface/timeout.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_textual_interface/widgets.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_tk_interface/__init__.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_tk_interface/date_entry.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_tk_interface/external_fix.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_tk_interface/facet.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_tk_interface/redirect_text_tkinter.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_tk_interface/timeout.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_tk_interface/utils.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_web_interface/__init__.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_web_interface/app.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_web_interface/child_adaptor.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_web_interface/parent_adaptor.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/cli.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/exceptions.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/experimental.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/facet/__init__.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/interfaces.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/settings.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/tag/__init__.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/tag/alias.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/tag/callback_tag.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/tag/datetime_tag.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/tag/flag.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/tag/internal.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/tag/path_tag.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/tag/secret_tag.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/tag/select_tag.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/tag/tag.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/tag/tag_factory.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/tag/type_stubs.py +0 -0
- {mininterface-1.2.1 → mininterface-1.2.2}/mininterface/validators.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mininterface
|
|
3
|
-
Version: 1.2.
|
|
3
|
+
Version: 1.2.2
|
|
4
4
|
Summary: CLI & dialog toolkit – a minimal interface to Python application (GUI, TUI, CLI + config files, web)
|
|
5
5
|
License: LGPL-3.0-or-later
|
|
6
6
|
License-File: LICENSE
|
|
@@ -191,8 +191,8 @@ These projects have the code base reduced thanks to the mininterface:
|
|
|
191
191
|
Take a look at the following example.
|
|
192
192
|
|
|
193
193
|
1. We define any Env class.
|
|
194
|
-
2. Then, we initialize mininterface with [`run(Env)`]
|
|
195
|
-
3. Then, we use various dialog methods, like [`confirm`]
|
|
194
|
+
2. Then, we initialize mininterface with [`run(Env)`](https://cz-nic.github.io/mininterface/run/) – the missing fields will be prompted for
|
|
195
|
+
3. Then, we use various dialog methods, like [`confirm`](https://cz-nic.github.io/mininterface/Mininterface/#mininterface.Mininterface.confirm), [`select`](https://cz-nic.github.io/mininterface/Mininterface/#mininterface.Mininterface.select) or [`form`](https://cz-nic.github.io/mininterface/Mininterface/#mininterface.Mininterface.form).
|
|
196
196
|
|
|
197
197
|
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...).
|
|
198
198
|
|
|
@@ -273,7 +273,7 @@ usage: program.py [-h] [OPTIONS]
|
|
|
273
273
|
|
|
274
274
|
You want to try out the Mininterface with your current [`ArgumentParser`](https://docs.python.org/3/library/argparse.html#argparse.ArgumentParser)?
|
|
275
275
|
|
|
276
|
-
You're using positional arguments, subparsers, types in the ArgumentParser... Mininterface will give you immediate benefit. Just wrap it inside the [`run`]
|
|
276
|
+
You're using positional arguments, subparsers, types in the ArgumentParser... Mininterface will give you immediate benefit. Just wrap it inside the [`run`](https://cz-nic.github.io/mininterface/run/) method.
|
|
277
277
|
|
|
278
278
|
```python
|
|
279
279
|
#!/usr/bin/env python3
|
|
@@ -330,7 +330,7 @@ Then, a `.form()` call will create a dialog with all the fields.
|
|
|
330
330
|
|
|
331
331
|

|
|
332
332
|
|
|
333
|
-
You will access the arguments through [`m.env`]
|
|
333
|
+
You will access the arguments through [`m.env`](https://cz-nic.github.io/mininterface/Mininterface/#mininterface.Mininterface.env)
|
|
334
334
|
|
|
335
335
|
```python
|
|
336
336
|
print(m.env.time) # -> 14:21
|
|
@@ -151,8 +151,8 @@ These projects have the code base reduced thanks to the mininterface:
|
|
|
151
151
|
Take a look at the following example.
|
|
152
152
|
|
|
153
153
|
1. We define any Env class.
|
|
154
|
-
2. Then, we initialize mininterface with [`run(Env)`]
|
|
155
|
-
3. Then, we use various dialog methods, like [`confirm`]
|
|
154
|
+
2. Then, we initialize mininterface with [`run(Env)`](https://cz-nic.github.io/mininterface/run/) – the missing fields will be prompted for
|
|
155
|
+
3. Then, we use various dialog methods, like [`confirm`](https://cz-nic.github.io/mininterface/Mininterface/#mininterface.Mininterface.confirm), [`select`](https://cz-nic.github.io/mininterface/Mininterface/#mininterface.Mininterface.select) or [`form`](https://cz-nic.github.io/mininterface/Mininterface/#mininterface.Mininterface.form).
|
|
156
156
|
|
|
157
157
|
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...).
|
|
158
158
|
|
|
@@ -233,7 +233,7 @@ usage: program.py [-h] [OPTIONS]
|
|
|
233
233
|
|
|
234
234
|
You want to try out the Mininterface with your current [`ArgumentParser`](https://docs.python.org/3/library/argparse.html#argparse.ArgumentParser)?
|
|
235
235
|
|
|
236
|
-
You're using positional arguments, subparsers, types in the ArgumentParser... Mininterface will give you immediate benefit. Just wrap it inside the [`run`]
|
|
236
|
+
You're using positional arguments, subparsers, types in the ArgumentParser... Mininterface will give you immediate benefit. Just wrap it inside the [`run`](https://cz-nic.github.io/mininterface/run/) method.
|
|
237
237
|
|
|
238
238
|
```python
|
|
239
239
|
#!/usr/bin/env python3
|
|
@@ -290,7 +290,7 @@ Then, a `.form()` call will create a dialog with all the fields.
|
|
|
290
290
|
|
|
291
291
|

|
|
292
292
|
|
|
293
|
-
You will access the arguments through [`m.env`]
|
|
293
|
+
You will access the arguments through [`m.env`](https://cz-nic.github.io/mininterface/Mininterface/#mininterface.Mininterface.env)
|
|
294
294
|
|
|
295
295
|
```python
|
|
296
296
|
print(m.env.time) # -> 14:21
|
|
@@ -1,20 +1,36 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import os
|
|
3
|
-
import re
|
|
4
|
-
from argparse import ArgumentParser
|
|
5
3
|
from dataclasses import fields, is_dataclass
|
|
6
4
|
from functools import lru_cache
|
|
7
5
|
from types import UnionType
|
|
8
|
-
from typing import
|
|
6
|
+
from typing import (
|
|
7
|
+
Any,
|
|
8
|
+
Annotated,
|
|
9
|
+
Callable,
|
|
10
|
+
Iterable,
|
|
11
|
+
Optional,
|
|
12
|
+
TypeVar,
|
|
13
|
+
Union,
|
|
14
|
+
Literal,
|
|
15
|
+
get_args,
|
|
16
|
+
get_origin,
|
|
17
|
+
get_type_hints,
|
|
18
|
+
)
|
|
9
19
|
|
|
10
20
|
from annotated_types import Ge, Gt, Le, Len, Lt, MultipleOf
|
|
11
21
|
|
|
12
22
|
logger = logging.getLogger(__name__)
|
|
13
23
|
|
|
14
24
|
try:
|
|
15
|
-
|
|
25
|
+
import tyro
|
|
26
|
+
from tyro._docstrings import get_field_docstring as _tyro_get_field_docstring
|
|
27
|
+
from tyro._docstrings import get_callable_description as _tyro_get_callable_description
|
|
28
|
+
|
|
29
|
+
_tyro_docstrings_available = True
|
|
16
30
|
except ImportError:
|
|
17
|
-
|
|
31
|
+
tyro = None
|
|
32
|
+
_tyro_docstrings_available = False
|
|
33
|
+
_tyro_get_callable_description = None
|
|
18
34
|
|
|
19
35
|
try:
|
|
20
36
|
from humanize import naturalsize as naturalsize_
|
|
@@ -70,40 +86,70 @@ def get_terminal_size():
|
|
|
70
86
|
return 0, 0
|
|
71
87
|
|
|
72
88
|
|
|
73
|
-
def
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
return
|
|
77
|
-
re.sub(r"\s\(positional\)$", "", action.dest).replace("-", "_"): re.sub(
|
|
78
|
-
r"\((default|fixed to|required).*\)", "", action.help or ""
|
|
79
|
-
)
|
|
80
|
-
for action in parser._actions
|
|
81
|
-
}
|
|
82
|
-
|
|
89
|
+
def get_class_description(obj) -> str:
|
|
90
|
+
if _tyro_get_callable_description:
|
|
91
|
+
return _tyro_get_callable_description(obj)
|
|
92
|
+
return ""
|
|
83
93
|
|
|
84
94
|
@lru_cache
|
|
85
|
-
def
|
|
86
|
-
|
|
87
|
-
|
|
95
|
+
def _get_descriptions_from_docstring(obj) -> dict[str, str]:
|
|
96
|
+
"""Extract field descriptions for all fields of a class.
|
|
97
|
+
|
|
98
|
+
Uses tyro's internal helptext extraction (tyro._docstrings.get_field_docstring),
|
|
99
|
+
which supports the same sources and precedence as tyro's own CLI generation:
|
|
100
|
+
1. tyro.conf.arg(help=...)
|
|
101
|
+
2. PEP 727 Doc
|
|
102
|
+
3. Docstrings (attribute docstrings or class docstring params)
|
|
103
|
+
4. Comments (inline or preceding)
|
|
104
|
+
|
|
105
|
+
We used to rely on tyro.extras.get_parser(), but that was marked deprecated,
|
|
106
|
+
so we call tyro's internal API directly instead.
|
|
107
|
+
"""
|
|
108
|
+
if not _tyro_docstrings_available:
|
|
109
|
+
return {}
|
|
110
|
+
|
|
111
|
+
result = {}
|
|
112
|
+
|
|
113
|
+
# Highest priority: tyro.conf.arg(help=...) in Annotated metadata.
|
|
114
|
+
try:
|
|
115
|
+
hints = get_type_hints(obj, include_extras=True)
|
|
116
|
+
ArgConfig = tyro.conf._confstruct._ArgConfig
|
|
117
|
+
for field_name, hint in hints.items():
|
|
118
|
+
if get_origin(hint) is Annotated:
|
|
119
|
+
for meta in hint.__metadata__:
|
|
120
|
+
if isinstance(meta, ArgConfig) and meta.help:
|
|
121
|
+
result[field_name] = meta.help
|
|
122
|
+
except Exception:
|
|
123
|
+
hints = {}
|
|
124
|
+
|
|
125
|
+
# Mid priority: docstrings and comments via tyro's own extraction.
|
|
126
|
+
for field_name in hints:
|
|
127
|
+
doc = _tyro_get_field_docstring(obj, field_name, ())
|
|
128
|
+
if doc:
|
|
129
|
+
result.setdefault(field_name, doc)
|
|
130
|
+
|
|
131
|
+
# Lowest priority: field.metadata["help"] from dynamically generated
|
|
132
|
+
# dataclasses (e.g. built from ArgumentParser via make_dataclass).
|
|
133
|
+
try:
|
|
134
|
+
for f in fields(obj): # type: ignore
|
|
135
|
+
if help_text := f.metadata.get("help"):
|
|
136
|
+
result.setdefault(f.name, help_text)
|
|
137
|
+
except TypeError:
|
|
138
|
+
pass
|
|
139
|
+
|
|
140
|
+
return result
|
|
88
141
|
|
|
89
142
|
|
|
90
143
|
def get_description(obj, param: str) -> str:
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
return d
|
|
101
|
-
else:
|
|
102
|
-
# We are missing mininterface[basic] requirement. Tyro is missing.
|
|
103
|
-
# Without tyro, we are not able to evaluate the class: m.form(Env),
|
|
104
|
-
# we can still evaluate its instance: m.form(Env()).
|
|
105
|
-
# However, without descriptions.
|
|
106
|
-
return ""
|
|
144
|
+
desc = _get_descriptions_from_docstring(obj).get(param, "")
|
|
145
|
+
if desc and desc.replace("-", "_") != param:
|
|
146
|
+
return desc
|
|
147
|
+
|
|
148
|
+
# We are missing mininterface[basic] requirement. Tyro is missing.
|
|
149
|
+
# Without tyro, we are not able to evaluate the class: m.form(Env),
|
|
150
|
+
# we can still evaluate its instance: m.form(Env()).
|
|
151
|
+
# However, without descriptions.
|
|
152
|
+
return ""
|
|
107
153
|
|
|
108
154
|
|
|
109
155
|
def yield_annotations(dataclass):
|
|
@@ -358,7 +404,7 @@ def strip_none(annotation):
|
|
|
358
404
|
args = tuple(arg for arg in get_args(annotation) if arg is not type(None))
|
|
359
405
|
if len(args) == 1:
|
|
360
406
|
return args[0]
|
|
361
|
-
return Union[args]
|
|
407
|
+
return Union[args]
|
|
362
408
|
|
|
363
409
|
return annotation
|
|
364
410
|
|
|
@@ -245,15 +245,7 @@ def parse_cli(
|
|
|
245
245
|
kwargs, None if helponly else m, args, type_form, env_classes, _custom_registry, annot, _req_fields
|
|
246
246
|
)
|
|
247
247
|
|
|
248
|
-
#
|
|
249
|
-
# 1. Getting the interface is a costly operation
|
|
250
|
-
# 2. There is this bug so that we need to use single interface:
|
|
251
|
-
# TODO
|
|
252
|
-
# As this works badly, lets make sure we use single interface now
|
|
253
|
-
# and will not need the second one.
|
|
254
|
-
# get_interface("gui")
|
|
255
|
-
# m = get_interface("gui")
|
|
256
|
-
# m.select([1,2,3])
|
|
248
|
+
# Make the interface ready for the user
|
|
257
249
|
m.env = env
|
|
258
250
|
except SystemExit as exception:
|
|
259
251
|
# --- (C) The dialog missing section ---
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import re
|
|
2
2
|
import warnings
|
|
3
|
-
from dataclasses import MISSING,
|
|
3
|
+
from dataclasses import MISSING, fields, is_dataclass
|
|
4
4
|
from types import UnionType
|
|
5
5
|
from typing import Annotated, Optional, Sequence, Type, Union, get_args, get_origin, TypeVar
|
|
6
6
|
|
|
7
|
-
|
|
8
7
|
try:
|
|
9
8
|
from tyro._singleton import MISSING_NONPROP
|
|
10
|
-
from tyro.extras import subcommand_type_from_defaults
|
|
11
9
|
|
|
12
10
|
from ..cli import SubcommandPlaceholder
|
|
13
11
|
except ImportError:
|
|
@@ -19,7 +17,7 @@ except ImportError:
|
|
|
19
17
|
from ..tag import Tag
|
|
20
18
|
from ..tag.tag_factory import tag_factory
|
|
21
19
|
from ..validators import not_empty
|
|
22
|
-
from .auxiliary import _get_origin,
|
|
20
|
+
from .auxiliary import _get_origin, get_class_description, get_description
|
|
23
21
|
from .form_dict import DataClass, EnvClass, MissingTagValue
|
|
24
22
|
|
|
25
23
|
# Pydantic is not a project dependency, that is just an optional integration
|
|
@@ -49,6 +47,15 @@ def coerce_type_to_annotation(value, annotation):
|
|
|
49
47
|
annotation = _unwrap_annotated(annotation) # NOTE might be superfluous, called before
|
|
50
48
|
origin = get_origin(annotation)
|
|
51
49
|
|
|
50
|
+
# Handle Union (e.g. int | None)
|
|
51
|
+
if origin in (Union, UnionType):
|
|
52
|
+
for arg in get_args(annotation):
|
|
53
|
+
try:
|
|
54
|
+
return coerce_type_to_annotation(value, arg)
|
|
55
|
+
except Exception:
|
|
56
|
+
pass
|
|
57
|
+
return value
|
|
58
|
+
|
|
52
59
|
# Handle tuple[...] conversion
|
|
53
60
|
if origin is tuple and isinstance(value, list):
|
|
54
61
|
args = get_args(annotation)
|
|
@@ -353,7 +360,7 @@ def choose_subcommand(env_classes: list[Type[DataClass]], m: "Mininterface[EnvCl
|
|
|
353
360
|
# NOTE make select display buttons if there is a little amount of options.
|
|
354
361
|
env = m.select(
|
|
355
362
|
{
|
|
356
|
-
(to_kebab_case(cl.__name__).replace("-", " ").capitalize(),
|
|
363
|
+
(to_kebab_case(cl.__name__).replace("-", " ").capitalize(), get_class_description(cl)): cl
|
|
357
364
|
for cl in env_classes
|
|
358
365
|
if cl is not SubcommandPlaceholder
|
|
359
366
|
}
|
|
@@ -339,16 +339,23 @@ def custom_parse_known_args(cf: CliFlags):
|
|
|
339
339
|
def _(self: TyroArgumentParser, args=None, namespace=None):
|
|
340
340
|
namespace, args = orig(self, args, namespace)
|
|
341
341
|
# NOTE We may check that the Env does not have its own `verbose``
|
|
342
|
-
# NOTE I do not like much tests need force=True here as they are run in paralel.
|
|
343
342
|
if cf.add_verbose and hasattr(namespace, "verbose"):
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
343
|
+
root = logging.getLogger()
|
|
344
|
+
if not root.handlers:
|
|
345
|
+
level = (
|
|
346
|
+
cf.get_log_level(namespace.verbose)
|
|
347
|
+
if namespace.verbose > 0
|
|
348
|
+
else cf.default_verbosity
|
|
347
349
|
)
|
|
348
|
-
else:
|
|
349
350
|
logging.basicConfig(
|
|
350
|
-
level=
|
|
351
|
+
level=level, format="%(message)s", stream=cf.orig_stream
|
|
351
352
|
)
|
|
353
|
+
elif namespace.verbose > 0:
|
|
354
|
+
level = cf.get_log_level(namespace.verbose)
|
|
355
|
+
root.setLevel(level)
|
|
356
|
+
for handler in root.handlers:
|
|
357
|
+
if handler.level > level: # increase verbosity for strict handlers
|
|
358
|
+
handler.setLevel(level)
|
|
352
359
|
delattr(namespace, "verbose")
|
|
353
360
|
|
|
354
361
|
if cf.add_version and hasattr(namespace, "version"):
|
|
@@ -365,9 +372,15 @@ def custom_parse_known_args(cf: CliFlags):
|
|
|
365
372
|
|
|
366
373
|
if cf.add_quiet and hasattr(namespace, "quiet"):
|
|
367
374
|
if namespace.quiet:
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
375
|
+
new_level = cf.get_log_level(-1)
|
|
376
|
+
root = logging.getLogger()
|
|
377
|
+
if not root.handlers:
|
|
378
|
+
logging.basicConfig(level=new_level, format="%(message)s", stream=cf.orig_stream)
|
|
379
|
+
else:
|
|
380
|
+
root.setLevel(new_level)
|
|
381
|
+
for handler in root.handlers:
|
|
382
|
+
if handler.level < new_level: # edit just benevolent handlers
|
|
383
|
+
handler.setLevel(new_level)
|
|
371
384
|
delattr(namespace, "quiet")
|
|
372
385
|
return namespace, args
|
|
373
386
|
|
|
@@ -32,7 +32,27 @@ class TkAdaptor(Tk, RichUiAdaptor, BackendAdaptor):
|
|
|
32
32
|
facet: TkFacet
|
|
33
33
|
settings: GuiSettings
|
|
34
34
|
|
|
35
|
+
_instance = None
|
|
36
|
+
""" singleton """
|
|
37
|
+
|
|
38
|
+
def __new__(cls, *args, **kwargs):
|
|
39
|
+
# Singleton.
|
|
40
|
+
# Why enforcing singleton?
|
|
41
|
+
# Invoking second tk would mean a strange second window
|
|
42
|
+
# and non-responding tkinter variables in the second invocation.
|
|
43
|
+
# get_interface("gui")
|
|
44
|
+
# m = get_interface("gui")
|
|
45
|
+
# m.select([1,2,3]) # cannot choose the value
|
|
46
|
+
if cls._instance is None:
|
|
47
|
+
return Tk.__new__(cls)
|
|
48
|
+
return cls._instance
|
|
49
|
+
|
|
35
50
|
def __init__(self, *args):
|
|
51
|
+
if self._instance:
|
|
52
|
+
return
|
|
53
|
+
else:
|
|
54
|
+
self.__class__._instance = self
|
|
55
|
+
|
|
36
56
|
BackendAdaptor.__init__(self, *args)
|
|
37
57
|
|
|
38
58
|
try:
|
|
@@ -40,6 +60,7 @@ class TkAdaptor(Tk, RichUiAdaptor, BackendAdaptor):
|
|
|
40
60
|
except TclError:
|
|
41
61
|
# even when installed the libraries are installed, display might not be available, hence tkinter fails
|
|
42
62
|
raise InterfaceNotAvailable
|
|
63
|
+
self._initialized = True
|
|
43
64
|
|
|
44
65
|
self.params = None
|
|
45
66
|
self._result = None
|
|
@@ -193,7 +193,8 @@ class SelectInputWrapper:
|
|
|
193
193
|
widget = AutoCombobox(self.frame, textvariable=self.variable)
|
|
194
194
|
widget["values"] = [k for k, *_ in options]
|
|
195
195
|
widget.pack()
|
|
196
|
-
widget.bind("<Return>", lambda _: "break") # override default enter that submits the form
|
|
196
|
+
#widget.bind("<Return>", lambda _: "break") # override default enter that submits the form
|
|
197
|
+
widget.bind("<Return>", lambda _: self._enter_handler())
|
|
197
198
|
|
|
198
199
|
self.set_default_label()
|
|
199
200
|
self.taking_focus = widget
|
|
@@ -212,3 +213,13 @@ class SelectInputWrapper:
|
|
|
212
213
|
# We never want to select the radiobutton in the initial phase
|
|
213
214
|
# as this might trigger on_change action (not caused by the user)
|
|
214
215
|
var.set(val)
|
|
216
|
+
|
|
217
|
+
def _enter_handler(self, event=None):
|
|
218
|
+
current_value = self.variable.get()
|
|
219
|
+
|
|
220
|
+
if not current_value:
|
|
221
|
+
return "break" # Let it perform the default behavior and open the dropdown
|
|
222
|
+
|
|
223
|
+
# If it has a value, submit it
|
|
224
|
+
self.adaptor._ok()
|
|
225
|
+
return "break"
|
|
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
|
|
|
4
4
|
|
|
5
5
|
[tool.poetry]
|
|
6
6
|
name = "mininterface"
|
|
7
|
-
version = "1.2.
|
|
7
|
+
version = "1.2.2"
|
|
8
8
|
description = "CLI & dialog toolkit – a minimal interface to Python application (GUI, TUI, CLI + config files, web)"
|
|
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
|
|
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.2.1 → mininterface-1.2.2}/mininterface/_textual_interface/button_contents.py
RENAMED
|
File without changes
|
|
File without changes
|
{mininterface-1.2.1 → mininterface-1.2.2}/mininterface/_textual_interface/file_picker_input.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
|
{mininterface-1.2.1 → mininterface-1.2.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
|
|
File without changes
|
|
File without changes
|