mininterface 1.1.4__tar.gz → 1.2.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 (74) hide show
  1. {mininterface-1.1.4 → mininterface-1.2.1}/PKG-INFO +2 -2
  2. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/__main__.py +0 -1
  3. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_lib/argparse_support.py +19 -6
  4. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_lib/auxiliary.py +11 -6
  5. mininterface-1.2.1/mininterface/_lib/cli_flags.py +234 -0
  6. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_lib/cli_parser.py +187 -47
  7. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_lib/cli_utils.py +1 -1
  8. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_lib/config_file.py +23 -21
  9. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_lib/dataclass_creation.py +137 -57
  10. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_lib/form_dict.py +13 -7
  11. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_lib/future_compatibility.py +2 -2
  12. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_lib/run.py +34 -27
  13. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_lib/start.py +4 -0
  14. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_lib/tyro_patches.py +79 -81
  15. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_mininterface/__init__.py +2 -5
  16. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_mininterface/adaptor.py +4 -5
  17. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_mininterface/mixin.py +1 -1
  18. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_text_interface/__init__.py +1 -1
  19. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_text_interface/timeout.py +16 -21
  20. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_textual_interface/widgets.py +1 -1
  21. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_tk_interface/select_input.py +1 -1
  22. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_tk_interface/timeout.py +2 -3
  23. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_web_interface/child_adaptor.py +1 -0
  24. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/cli.py +2 -1
  25. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/exceptions.py +16 -0
  26. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/tag/datetime_tag.py +1 -1
  27. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/tag/flag.py +4 -2
  28. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/tag/tag.py +23 -7
  29. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/tag/tag_factory.py +5 -2
  30. {mininterface-1.1.4 → mininterface-1.2.1}/pyproject.toml +2 -2
  31. mininterface-1.1.4/mininterface/_lib/cli_flags.py +0 -107
  32. {mininterface-1.1.4 → mininterface-1.2.1}/LICENSE +0 -0
  33. {mininterface-1.1.4 → mininterface-1.2.1}/README.md +0 -0
  34. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/__init__.py +0 -0
  35. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_lib/__init__.py +0 -0
  36. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_lib/redirectable.py +0 -0
  37. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_lib/shortcuts.py +0 -0
  38. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_lib/showcase.py +0 -0
  39. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_text_interface/adaptor.py +0 -0
  40. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_text_interface/facet.py +0 -0
  41. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_textual_interface/__init__.py +0 -0
  42. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_textual_interface/adaptor.py +0 -0
  43. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_textual_interface/button_contents.py +0 -0
  44. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_textual_interface/facet.py +0 -0
  45. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_textual_interface/file_picker_input.py +0 -0
  46. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_textual_interface/form_contents.py +0 -0
  47. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_textual_interface/secret_input.py +0 -0
  48. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_textual_interface/style.tcss +0 -0
  49. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_textual_interface/textual_app.py +0 -0
  50. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_textual_interface/timeout.py +0 -0
  51. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_tk_interface/__init__.py +0 -0
  52. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_tk_interface/adaptor.py +0 -0
  53. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_tk_interface/date_entry.py +0 -0
  54. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_tk_interface/external_fix.py +0 -0
  55. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_tk_interface/facet.py +0 -0
  56. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_tk_interface/redirect_text_tkinter.py +0 -0
  57. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_tk_interface/secret_entry.py +0 -0
  58. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_tk_interface/utils.py +0 -0
  59. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_web_interface/__init__.py +0 -0
  60. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_web_interface/app.py +0 -0
  61. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/_web_interface/parent_adaptor.py +0 -0
  62. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/experimental.py +0 -0
  63. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/facet/__init__.py +0 -0
  64. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/interfaces.py +0 -0
  65. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/settings.py +1 -1
  66. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/tag/__init__.py +0 -0
  67. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/tag/alias.py +0 -0
  68. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/tag/callback_tag.py +0 -0
  69. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/tag/internal.py +0 -0
  70. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/tag/path_tag.py +0 -0
  71. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/tag/secret_tag.py +0 -0
  72. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/tag/select_tag.py +0 -0
  73. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/tag/type_stubs.py +0 -0
  74. {mininterface-1.1.4 → mininterface-1.2.1}/mininterface/validators.py +0 -0
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mininterface
3
- Version: 1.1.4
4
- Summary: A minimal access to GUI, TUI, CLI and config
3
+ Version: 1.2.1
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
7
7
  Author: Edvard Rejthar
@@ -1,4 +1,3 @@
1
- from ast import literal_eval
2
1
  from dataclasses import dataclass
3
2
  from os import environ
4
3
  from pathlib import Path
@@ -1,9 +1,19 @@
1
1
  import re
2
2
  import sys
3
- from argparse import (SUPPRESS, Action, ArgumentParser, _AppendAction,
4
- _AppendConstAction, _CountAction, _HelpAction,
5
- _StoreConstAction, _StoreFalseAction, _StoreTrueAction,
6
- _SubParsersAction, _VersionAction)
3
+ from argparse import (
4
+ SUPPRESS,
5
+ Action,
6
+ ArgumentParser,
7
+ _AppendAction,
8
+ _AppendConstAction,
9
+ _CountAction,
10
+ _HelpAction,
11
+ _StoreConstAction,
12
+ _StoreFalseAction,
13
+ _StoreTrueAction,
14
+ _SubParsersAction,
15
+ _VersionAction,
16
+ )
7
17
  from collections import defaultdict
8
18
  from dataclasses import MISSING, Field, dataclass, field, make_dataclass
9
19
  from functools import cached_property
@@ -65,7 +75,9 @@ class ArgparseField:
65
75
  return self.action.dest in self.properties
66
76
 
67
77
 
68
- def parser_to_dataclass(parser: ArgumentParser, name: str = "Args") -> tuple[DataClass | list[DataClass], Optional[str]]:
78
+ def parser_to_dataclass(
79
+ parser: ArgumentParser, name: str = "Args"
80
+ ) -> tuple[DataClass | list[DataClass], Optional[str]]:
69
81
  """
70
82
  Note: Ex. parser.add_argument("--time", type=time) -> does work at all in argparse, here it works.
71
83
 
@@ -197,8 +209,9 @@ def _make_dataclass_from_actions(
197
209
  if action.choices:
198
210
  # With the drop of Python 3.10, use mere:
199
211
  # arg_type = Literal[*action.choices]
200
- if sys.version_info >= (3,11):
212
+ if sys.version_info >= (3, 11):
201
213
  from .future_compatibility import literal
214
+
202
215
  arg_type = literal(action.choices)
203
216
  else:
204
217
  # we do not prefer this option as tyro does not understand it
@@ -5,8 +5,7 @@ from argparse import ArgumentParser
5
5
  from dataclasses import fields, is_dataclass
6
6
  from functools import lru_cache
7
7
  from types import UnionType
8
- from typing import (Any, Callable, Iterable, Optional, TypeVar, Union, Literal,
9
- get_args, get_origin, get_type_hints)
8
+ from typing import Any, Callable, Iterable, Optional, TypeVar, Union, Literal, get_args, get_origin, get_type_hints
10
9
 
11
10
  from annotated_types import Ge, Gt, Le, Len, Lt, MultipleOf
12
11
 
@@ -75,7 +74,9 @@ def get_descriptions(parser: ArgumentParser) -> dict:
75
74
  """Load descriptions from the parser. Strip argparse info about the default value as it will be editable in the form."""
76
75
  # clean-up tyro stuff that may have a meaning in the CLI, but not in the UI
77
76
  return {
78
- re.sub(r"\s\(positional\)$", "", action.dest).replace("-", "_"): re.sub(r"\((default|fixed to|required).*\)", "", action.help or "")
77
+ re.sub(r"\s\(positional\)$", "", action.dest).replace("-", "_"): re.sub(
78
+ r"\((default|fixed to|required).*\)", "", action.help or ""
79
+ )
79
80
  for action in parser._actions
80
81
  }
81
82
 
@@ -201,7 +202,7 @@ def matches_annotation(value, annotation) -> bool:
201
202
  if origin is list:
202
203
  return all(matches_annotation(item, subtypes[0]) for item in value)
203
204
  elif origin is tuple:
204
- if len(subtypes) == 2 and subtypes[1] is Ellipsis: # ex. tuple[int, ...]
205
+ if len(subtypes) == 2 and subtypes[1] is Ellipsis: # ex. tuple[int, ...]
205
206
  return all(matches_annotation(v, subtypes[0]) for v in value)
206
207
  if len(subtypes) != len(value):
207
208
  return False
@@ -286,8 +287,9 @@ def merge_dicts(d1: dict, d2: dict):
286
287
  d1[key] = value
287
288
  return d1
288
289
 
290
+
289
291
  def dict_diff(a: dict, b: dict) -> dict:
290
- """ Returns the B values where they differ. """
292
+ """Returns the B values where they differ."""
291
293
  result = {}
292
294
  for k in b:
293
295
  if isinstance(a.get(k), dict) and isinstance(b.get(k), dict):
@@ -298,6 +300,7 @@ def dict_diff(a: dict, b: dict) -> dict:
298
300
  result[k] = b[k]
299
301
  return result
300
302
 
303
+
301
304
  def naturalsize(value: float | str, *args) -> str:
302
305
  """For a bare interface, humanize might not be installed."""
303
306
  if naturalsize_:
@@ -346,6 +349,7 @@ def allows_none(annotation) -> bool:
346
349
  return any(arg is type(None) for arg in args)
347
350
  return False
348
351
 
352
+
349
353
  def strip_none(annotation):
350
354
  """Return the same annotation but without NoneType inside a Union/Optional."""
351
355
  origin = get_origin(annotation)
@@ -358,7 +362,8 @@ def strip_none(annotation):
358
362
 
359
363
  return annotation
360
364
 
361
- @lru_cache(maxsize=1024*10)
365
+
366
+ @lru_cache(maxsize=1024 * 10)
362
367
  def _get_origin(tp: Any):
363
368
  """
364
369
  Cached version of typing.get_origin.
@@ -0,0 +1,234 @@
1
+ from argparse import ArgumentParser
2
+ import logging
3
+ import sys
4
+ from typing import Optional, Sequence
5
+
6
+ from tyro.conf import FlagConversionOff
7
+
8
+ from .form_dict import EnvClass
9
+
10
+ from typing import List, Any, Optional
11
+
12
+ from tyro._fields import FieldDefinition
13
+ from tyro.conf._confstruct import _ArgConfig
14
+
15
+
16
+ class CliFlags:
17
+
18
+ _add_verbose: bool = False
19
+ version: str = ""
20
+ _add_quiet: bool = False
21
+
22
+ default_verbosity: int = logging.WARNING
23
+ _verbosity_sequence: Optional[Sequence[int]] = None
24
+
25
+ config: bool = False
26
+
27
+ def __init__(
28
+ self,
29
+ add_verbose: bool | int | Sequence[int] = False,
30
+ add_version: Optional[str] = None,
31
+ add_version_package: Optional[str] = None,
32
+ add_quiet: bool = False,
33
+ add_config: bool = False,
34
+ ):
35
+ self._enabled = {"verbose": True, "version": True, "quiet": True, "config": True}
36
+ # verbosity
37
+ match add_verbose:
38
+ case bool():
39
+ self._add_verbose = add_verbose
40
+ case int():
41
+ self._add_verbose = True
42
+ self.default_verbosity = add_verbose
43
+ self._verbosity_sequence = list(range(add_verbose - 10, -1, -10))
44
+ case list() | tuple():
45
+ self._add_verbose = True
46
+ self.default_verbosity = add_verbose[0]
47
+ self._verbosity_sequence = add_verbose[1:]
48
+ self._add_quiet = add_quiet
49
+
50
+ # version
51
+ if add_version:
52
+ self.version = add_version
53
+ elif add_version_package:
54
+ try:
55
+ from importlib.metadata import PackageNotFoundError, version
56
+ except ImportError:
57
+ self.version = f"cannot determine version"
58
+ try:
59
+ self.version = version(add_version_package)
60
+ except PackageNotFoundError:
61
+ self.version = f"package {add_version_package} not found"
62
+
63
+ # config
64
+ self.config = add_config
65
+
66
+ self.orig_stream = (
67
+ sys.stderr
68
+ ) # NOTE might be removed now. Might be used if we redirect_stderr while setting basicConfig.
69
+
70
+ self.field_list: list[FieldDefinition] = []
71
+ """ List of FieldDefinitions corresponding to the arguments added via this helper"""
72
+
73
+ self.arguments_prepared: list[dict[str, Any]] = []
74
+ self.setup_done = False
75
+ """ Setup might be called multiple times – ex. parsing fails and we call tyro.cli in recursion. """
76
+
77
+ def should_add(self, env_classes: list[EnvClass]) -> bool:
78
+ # Flags are added only if neither the env_class nor any of the subcommands have the same-name flag already
79
+ self._enabled["verbose"] = self._add_verbose and self._attr_not_present("verbose", env_classes)
80
+ self._enabled["quiet"] = self._add_quiet and self._attr_not_present("quiet", env_classes)
81
+ self._enabled["version"] = self.version and self._attr_not_present("version", env_classes)
82
+ self._enabled["config"] = self.config and self._attr_not_present("config", env_classes)
83
+
84
+ return self.add_verbose or self.add_version or self.add_quiet or self.add_config
85
+
86
+ def _attr_not_present(self, flag, env_classes):
87
+ return all(flag not in cl.__annotations__ for cl in env_classes)
88
+
89
+ @property
90
+ def add_verbose(self):
91
+ return self._add_verbose and self._enabled["verbose"]
92
+
93
+ @property
94
+ def add_version(self):
95
+ return self.version and self._enabled["version"]
96
+
97
+ @property
98
+ def add_quiet(self):
99
+ return self._add_quiet and self._enabled["quiet"]
100
+
101
+ @property
102
+ def add_config(self):
103
+ return self.config and self._enabled["config"]
104
+
105
+ def get_log_level(self, count):
106
+ """
107
+ Ex.
108
+ * add_verbose = True ( default level = WARNING )
109
+ * -v -> logging.INFO
110
+ * -vv -> logging.DEBUG
111
+ * -vvv -> logging.NOTSET
112
+ * add_verbose = default level INFO
113
+ * -v -> logging.DEBUG
114
+ * -vv -> logging.NOTSET
115
+ * add_verbose = (40, 35, 30, 25)
116
+ * -v -> 35
117
+ * -vv -> logging.INFO
118
+ * -vvv -> 25
119
+ * -vvv -> logging.NOTSET
120
+
121
+ Args:
122
+ count: number of times `--verbose` flag is used. Negative count means the `--quiet` flag is used.
123
+
124
+ Returns:
125
+ int: log level
126
+ """
127
+ if count == -1: # quiet flag
128
+ return logging.ERROR
129
+ if not count:
130
+ return self.default_verbosity
131
+ if not self._verbosity_sequence:
132
+ seq = logging.INFO, logging.DEBUG
133
+ else:
134
+ seq = self._verbosity_sequence
135
+ log_level = {i + 1: level for i, level in enumerate(seq)}.get(count, logging.NOTSET)
136
+ return log_level
137
+
138
+ def add_typed_argument(
139
+ self,
140
+ prefix: str,
141
+ *aliases: str,
142
+ action: Optional[str] = None,
143
+ default: Any = False,
144
+ helptext: Optional[str] = None,
145
+ metavar: Optional[str] = None,
146
+ version: Optional[str] = None,
147
+ ) -> FieldDefinition:
148
+ # Prepare FieldDefinition
149
+ name = aliases[0]
150
+ aliases_ = tuple((prefix * (1 if len(n) == 1 else 2) + n) for n in aliases) if aliases else None
151
+ typ_ = bool if action in ("store_true", "store_false") else int if action == "count" else str
152
+
153
+ field = FieldDefinition(
154
+ intern_name=name,
155
+ extern_name=name,
156
+ type=typ_,
157
+ type_stripped=typ_,
158
+ default=default,
159
+ helptext=helptext,
160
+ markers={FlagConversionOff},
161
+ custom_constructor=False,
162
+ argconf=_ArgConfig(
163
+ name=aliases_[0],
164
+ metavar="",
165
+ help=helptext,
166
+ help_behavior_hint="",
167
+ aliases=aliases_[1:] or None,
168
+ prefix_name=False,
169
+ constructor_factory=None,
170
+ default=default,
171
+ ),
172
+ mutex_group=None,
173
+ call_argname=name,
174
+ )
175
+
176
+ self.field_list.append(field)
177
+
178
+ # prepare argparse
179
+ self.arguments_prepared.append(
180
+ {
181
+ "field": field,
182
+ "names": aliases_,
183
+ "kwargs": {
184
+ "action": action,
185
+ "default": default,
186
+ "help": helptext,
187
+ "metavar": metavar,
188
+ "version": version,
189
+ },
190
+ }
191
+ )
192
+
193
+ return field
194
+
195
+ def setup(self, parser: ArgumentParser):
196
+ if self.setup_done:
197
+ # tyro.cli might be called multiple times if some missing required fields
198
+ return
199
+ self.setup_done = True
200
+ prefix = "-" if "-" in parser.prefix_chars else parser.prefix_chars[0]
201
+ if self.add_verbose:
202
+ self.add_typed_argument(
203
+ prefix,
204
+ "verbose",
205
+ "v",
206
+ action="count",
207
+ default=0,
208
+ helptext="verbosity level, can be used multiple times to increase",
209
+ )
210
+
211
+ if self.add_version:
212
+ self.add_typed_argument(
213
+ prefix,
214
+ "version",
215
+ action="version",
216
+ version=self.version,
217
+ default="",
218
+ helptext=f"show program's version number ({self.version}) and exit",
219
+ )
220
+
221
+ if self.add_quiet:
222
+ self.add_typed_argument(
223
+ prefix, "quiet", "q", action="store_true", helptext="suppress warnings, display only errors"
224
+ )
225
+
226
+ if self.add_config:
227
+ self.add_typed_argument(
228
+ prefix, "config", helptext=f"path to config file to fetch the defaults from", metavar="PATH"
229
+ )
230
+
231
+ def apply_to_parser(self, parser):
232
+ for item in self.arguments_prepared:
233
+ kwargs = {k: v for k, v in item["kwargs"].items() if v is not None}
234
+ parser.add_argument(*item["names"], **kwargs)