mininterface 0.1.1__tar.gz → 0.4.0__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mininterface
3
- Version: 0.1.1
3
+ Version: 0.4.0
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
@@ -22,6 +22,7 @@ Description-Content-Type: text/markdown
22
22
 
23
23
  # Mininterface – access to GUI, TUI, CLI and config files
24
24
  [![License: GPL v3](https://img.shields.io/badge/License-GPLv3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0)
25
+ [![Build Status](https://github.com/CZ-NIC/mininterface/actions/workflows/run-unittest.yml/badge.svg)](https://github.com/CZ-NIC/mininterface/actions)
25
26
 
26
27
  Write the program core, do not bother with the input/output.
27
28
 
@@ -1,5 +1,6 @@
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
+ [![Build Status](https://github.com/CZ-NIC/mininterface/actions/workflows/run-unittest.yml/badge.svg)](https://github.com/CZ-NIC/mininterface/actions)
3
4
 
4
5
  Write the program core, do not bother with the input/output.
5
6
 
@@ -11,7 +11,7 @@ except ImportError:
11
11
 
12
12
 
13
13
  from .common import InterfaceNotAvailable
14
- from .auxiliary import FormDict, RedirectText, dataclass_to_dict, dict_to_dataclass, recursive_set_focus, normalize_types
14
+ from .auxiliary import FormDict, RedirectText, config_to_dict, config_from_dict, recursive_set_focus, normalize_types
15
15
  from .Mininterface import Cancelled, ConfigInstance, Mininterface
16
16
 
17
17
 
@@ -46,11 +46,11 @@ class GuiInterface(Mininterface):
46
46
 
47
47
  def ask_args(self) -> ConfigInstance:
48
48
  """ Display a window form with all parameters. """
49
- params_ = dataclass_to_dict(self.args, self.descriptions)
49
+ params_ = config_to_dict(self.args, self.descriptions)
50
50
 
51
51
  # fetch the dict of dicts values from the form back to the namespace of the dataclasses
52
52
  data = self.window.run_dialog(params_)
53
- dict_to_dataclass(self.args, data)
53
+ config_from_dict(self.args, data)
54
54
  return self.args
55
55
 
56
56
  def ask_form(self, args: FormDict, title: str = "") -> dict:
@@ -1,5 +1,5 @@
1
1
  from pprint import pprint
2
- from .auxiliary import ConfigInstance, FormDict, dataclass_to_dict, dict_to_dataclass
2
+ from .auxiliary import ConfigInstance, FormDict, config_to_dict, config_from_dict
3
3
  from .Mininterface import Cancelled, Mininterface
4
4
 
5
5
 
@@ -41,9 +41,10 @@ FormDict = dict[str, Union[Value, Any, 'FormDict']]
41
41
  """ Nested form that can have descriptions (through Value) instead of plain values. """
42
42
 
43
43
 
44
- def dataclass_to_dict(args: ConfigInstance, descr: dict, _path="") -> FormDict:
44
+ def config_to_dict(args: ConfigInstance, descr: dict, _path="") -> FormDict:
45
45
  """ Convert the dataclass produced by tyro into dict of dicts. """
46
46
  main = ""
47
+ # print(args)# TODO
47
48
  params = {main: {}} if not _path else {}
48
49
  for param, val in vars(args).items():
49
50
  annotation = None
@@ -64,11 +65,12 @@ def dataclass_to_dict(args: ConfigInstance, descr: dict, _path="") -> FormDict:
64
65
  logger.warn(f"Annotation {wanted_type} of `{param}` not supported by Mininterface."
65
66
  "None converted to False.")
66
67
  if hasattr(val, "__dict__"): # nested config hierarchy
67
- params[param] = dataclass_to_dict(val, descr, _path=f"{_path}{param}.")
68
+ params[param] = config_to_dict(val, descr, _path=f"{_path}{param}.")
68
69
  elif not _path: # scalar value in root
69
70
  params[main][param] = Value(val, descr.get(param), annotation)
70
71
  else: # scalar value in nested
71
72
  params[param] = Value(val, descr.get(f"{_path}{param}"), annotation)
73
+ # print(params) # TODO
72
74
  return params
73
75
 
74
76
 
@@ -76,34 +78,53 @@ def normalize_types(origin: FormDict, data: dict) -> dict:
76
78
  """ Run validators of all Value objects. If fails, outputs info.
77
79
  Return corrected data. (Ex: Some values might be nulled from "".)
78
80
  """
79
- for (group, params), params2 in zip(data.items(), origin.values()):
80
- for (key, val), pattern in zip(params.items(), params2.values()):
81
- if isinstance(pattern, Value) and pattern.annotation:
82
- if val == "" and type(None) in get_args(pattern.annotation):
83
- # The user is not able to set the value to None, they left it empty.
84
- # Cast back to None as None is one of the allowed types.
85
- # Ex: `severity: int | None = None`
86
- data[group][key] = val = None
87
- elif pattern.annotation == Optional[int]:
88
- try:
89
- data[group][key] = val = int(val)
90
- except ValueError:
91
- pass
92
-
93
- if not isinstance(val, pattern.annotation):
94
- pattern.set_error_text(f"Type must be `{pattern.annotation}`!")
95
- return False
81
+ def check(ordict, orkey, orval, dataPos: dict, dataKey, val):
82
+ if isinstance(orval, Value) and orval.annotation:
83
+ if val == "" and type(None) in get_args(orval.annotation):
84
+ # The user is not able to set the value to None, they left it empty.
85
+ # Cast back to None as None is one of the allowed types.
86
+ # Ex: `severity: int | None = None`
87
+ dataPos[dataKey] = val = None
88
+ elif orval.annotation == Optional[int]:
89
+ try:
90
+ dataPos[dataKey] = val = int(val)
91
+ except ValueError:
92
+ pass
93
+
94
+ if not isinstance(val, orval.annotation):
95
+ orval.set_error_text(f"Type must be `{orval.annotation}`!")
96
+ raise RuntimeError # revision needed
97
+
98
+ # keep values if revision needed
99
+ # We merge new data to the origin. If form is re-submitted, the values will stay there.
100
+ if isinstance(orval, Value):
101
+ orval.val = val
102
+ else:
103
+ ordict[orkey] = val
104
+
105
+ try:
106
+ for (key1, val1), (orkey1, orval1) in zip(data.items(), origin.items()):
107
+ if isinstance(val1, dict): # nested config hierarchy
108
+ # NOTE: This allows only single nested dict.
109
+ for (key2, val2), (orkey2, orval2) in zip(val1.items(), orval1.items()):
110
+ check(orval1, orkey2, orval2, data[key1], key2, val2)
111
+ else:
112
+ check(origin, orkey1, orval1, data, key1, val1)
113
+ except RuntimeError:
114
+ return False
115
+
96
116
  return data
97
117
 
98
118
 
99
- def dict_to_dataclass(args: ConfigInstance, data: dict):
100
- """ Convert the dict of dicts from the GUI back into the object holding the configuration. """
119
+ def config_from_dict(args: ConfigInstance, data: dict):
120
+ """ Fetch back data.
121
+ Merge the dict of dicts from the GUI back into the object holding the configuration. """
101
122
  for group, params in data.items():
102
123
  for key, val in params.items():
103
124
  if group:
104
- setattr(getattr(args, group), key, val)
125
+ setattr(getattr(args, group), key, val.val if isinstance(val, Value) else val)
105
126
  else:
106
- setattr(args, key, val)
127
+ setattr(args, key, val.val if isinstance(val, Value) else val)
107
128
 
108
129
 
109
130
  def get_terminal_size():
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
4
4
 
5
5
  [tool.poetry]
6
6
  name = "mininterface"
7
- version = "0.1.1"
7
+ version = "0.4.0"
8
8
  description = "A minimal access to GUI, TUI, CLI and config"
9
9
  authors = ["Edvard Rejthar <edvard.rejthar@nic.cz>"]
10
10
  license = "GPL-3.0-or-later"