unisi 0.1.1__py3-none-any.whl → 0.1.3__py3-none-any.whl

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.
unisi/autotest.py CHANGED
@@ -4,7 +4,7 @@ from .guielements import *
4
4
  from .containers import Block, Dialog
5
5
  from .users import User
6
6
  from .common import *
7
- from jsoncomparison import Compare, NO_DIFF
7
+ from .jsoncomparison import Compare, NO_DIFF
8
8
 
9
9
  #setting config variables
10
10
  testdir = 'autotest'
@@ -34,7 +34,7 @@ logfile = config.logfile
34
34
  handlers = [logging.FileHandler(logfile), logging.StreamHandler()] if logfile else []
35
35
  logging.basicConfig(level = logging.WARNING, format = format, handlers = handlers)
36
36
 
37
- comparator = Compare().check
37
+ comparator = Compare(rules = {'toolbar': '*'}).check
38
38
 
39
39
  def jsonString(obj):
40
40
  pretty = config.pretty_print
@@ -44,15 +44,13 @@ class Recorder:
44
44
  def __init__(self):
45
45
  self.start(None)
46
46
 
47
- def accept(self, msg, response):
48
- if self.ignored_1message:
47
+ def accept(self, msg, response):
48
+ if not self.ignored_1message:
49
+ self.ignored_1message = True
50
+ else:
49
51
  self.record_buffer.append(f"{jsonString(msg)},\
50
- \n{'null' if response is None else jsonString(response)}\n")
51
- else: #start for setting screen
52
- self.record_buffer.append(jsonString(ArgObject(block = 'root',
53
- element = None, value = User.last_user.screen_module.name)))
54
- self.ignored_1message = True
55
-
52
+ \n{'null' if response is None else jsonString(response)}\n")
53
+
56
54
  def stop_recording(self, _, x):
57
55
  button.spinner = None
58
56
  button.changed = button_clicked
@@ -69,44 +67,69 @@ class Recorder:
69
67
  Warning('Nothing to save!',button)
70
68
 
71
69
  def start(self,fname):
72
- self.record_file = fname
73
- self.ignored_1message = False
70
+ self.record_file = fname
74
71
  self.record_buffer = []
72
+ if fname:
73
+ self.ignored_1message = True
74
+ module = User.last_user.screen_module
75
+ self.accept(ArgObject(block = 'root', element = None,
76
+ event = 'changed', value = module.name), module.screen)
77
+ self.ignored_1message = False
75
78
 
76
79
  recorder = Recorder()
77
80
 
78
- def obj2pyjson(obj):
81
+ def obj2json(obj):
79
82
  return json.loads(jsonpickle.encode(obj,unpicklable=False))
80
83
 
81
84
  def test(filename, user):
82
85
  filepath = f'{testdir}{divpath}{filename}'
83
86
  file = open(filepath, "r")
84
- data = json.loads(file.read())
87
+ data = json.loads(file.read())
85
88
  error = False
86
- for message in data:
87
- if message is not None and message.get('block'):
88
- result = user.result4message(ReceivedMessage(message))
89
- response = user.prepare_result(result)
90
- user_message = message
91
- else:
92
- diff = comparator(message, obj2pyjson(response))
93
- if diff != NO_DIFF:
94
- print(f"\nTest {filename} is failed on message {user_message}:")
95
- err = diff.get('_message')
96
- if err:
97
- print(f" {err}")
98
- else:
99
- for value in diff.values():
100
- err = value['_message']
101
- print(f" {err}")
102
- error = True
89
+ for i in range(0, len(data), 2):
90
+ message = data[i]
91
+ expected = data[i + 1]
92
+
93
+ result = user.result4message(ReceivedMessage(message))
94
+ responce = user.prepare_result(result)
95
+ jresponce = obj2json(responce)
96
+
97
+ diff = comparator(expected, jresponce)
98
+ if diff != NO_DIFF:
99
+ print(f"\nTest {filename} is failed on message {message}:")
100
+ err = diff.get('_message')
101
+ if err:
102
+ print(f" {err}")
103
+ else:
104
+ for key, obj in diff.items():
105
+ if key != '#name':
106
+ while True:
107
+ err = obj.get('_message')
108
+ if err:
109
+ print(f" {err} \n")
110
+ break
111
+ else:
112
+ content = obj.get('_content')
113
+ if content and len(obj) == 1:
114
+ obj = content
115
+ else:
116
+ for key, subobj in obj.items():
117
+ if key != '_content':
118
+ if isinstance(key, str):
119
+ name = obj.get('#name', '')
120
+ if name:
121
+ key = f' {name}: {key}'
122
+ print(f" {key}")
123
+ obj = subobj
124
+ break
125
+ error = True
103
126
  return not error
104
127
 
105
128
  test_name = Edit('Name test file', '', focus = True)
106
129
  rewrite = Switch('Overwrite existing', False, type = 'check')
107
130
 
108
131
  def button_clicked(_,__):
109
- test_name.value = ''
132
+ test_name.value = User.last_user.screen.name
110
133
  test_name.complete = smart_complete(os.listdir(testdir))
111
134
  return Dialog('Create autotest..', ask_create_test, test_name, rewrite)
112
135
 
@@ -152,19 +175,19 @@ def check_block(self):
152
175
  child_names.add(child.name)
153
176
  return errors
154
177
 
155
- def check_screen(module):
156
- self = module.screen
178
+ def check_module(module):
179
+ screen = module.screen
157
180
  errors = []
158
181
  block_names = set()
159
- if not hasattr(self, 'name') or not self.name:
182
+ if not hasattr(screen, 'name') or not screen.name:
160
183
  errors.append(f"Screen file {module.__file__} does not contain name!")
161
- self.name = 'Unknown'
162
- elif not isinstance(self.name, str):
163
- errors.append(f"The name in screen file {module.__file__} {self.name} is not a string!")
164
- if not isinstance(self.blocks, list):
184
+ screen.name = 'Unknown'
185
+ elif not isinstance(screen.name, str):
186
+ errors.append(f"The name in screen file {module.__file__} {screen.name} is not a string!")
187
+ if not isinstance(screen.blocks, list):
165
188
  errors.append(f"Screen file {module.__file__} does not contain 'blocks' list!")
166
189
  else:
167
- for bl in flatten(self.blocks):
190
+ for bl in flatten(screen.blocks):
168
191
  if not isinstance(bl, Block):
169
192
  errors.append(f'The screen contains invalid element {bl} instead of Block object!')
170
193
  elif bl.name in block_names:
@@ -173,7 +196,7 @@ def check_screen(module):
173
196
  block_names.add(bl.name)
174
197
  errors += check_block(bl)
175
198
  if errors:
176
- errors.insert(0, f"\nErrors in screen {self.name}, file name {module.__file__}:")
199
+ errors.insert(0, f"\nErrors in screen {screen.name}, file name {module.__file__}:")
177
200
  return errors
178
201
 
179
202
  def run_tests():
@@ -184,9 +207,9 @@ def run_tests():
184
207
  user.session = 'autotest'
185
208
  errors = []
186
209
  for module in user.screens:
187
- errors += check_screen(module)
210
+ errors += check_module(module)
188
211
  if errors:
189
- errors.insert(0, f'\n!!----Unisi detected errors in screens:')
212
+ errors.insert(0, f'\n!!----Detected errors in screens:')
190
213
  print('\n'.join(errors), '\n')
191
214
  elif user.screens:
192
215
  print(f'\n----The screen definitions are correct.-----\n')
@@ -0,0 +1,21 @@
1
+ from .compare import NO_DIFF, Compare
2
+ from .errors import (
3
+ KeyNotExist,
4
+ LengthsNotEqual,
5
+ TypesNotEqual,
6
+ UnexpectedKey,
7
+ ValueNotFound,
8
+ ValuesNotEqual,
9
+ )
10
+
11
+ __all__ = (
12
+ "Compare",
13
+ "NO_DIFF",
14
+
15
+ "ValuesNotEqual",
16
+ "TypesNotEqual",
17
+ "KeyNotExist",
18
+ "ValueNotFound",
19
+ "LengthsNotEqual",
20
+ "UnexpectedKey",
21
+ )
@@ -0,0 +1,233 @@
1
+ import copy
2
+ import json
3
+ from typing import Optional
4
+
5
+ from .config import Config
6
+ from .errors import (
7
+ KeyNotExist,
8
+ LengthsNotEqual,
9
+ TypesNotEqual,
10
+ UnexpectedKey,
11
+ ValueNotFound,
12
+ ValuesNotEqual,
13
+ )
14
+ from .ignore import Ignore
15
+
16
+ NO_DIFF: dict = {}
17
+ NO_RULES: dict = {}
18
+
19
+ DEFAULT_CONFIG = {
20
+ 'output': {
21
+ 'console': False,
22
+ 'file': {
23
+ 'allow_nan': True,
24
+ 'ensure_ascii': True,
25
+ 'indent': 4,
26
+ 'name': None,
27
+ 'skipkeys': True,
28
+ },
29
+ },
30
+ 'types': {
31
+ 'float': {
32
+ 'allow_round': 2,
33
+ },
34
+ 'list': {
35
+ 'check_length': True,
36
+ },
37
+ },
38
+ }
39
+
40
+
41
+ class Compare:
42
+
43
+ __slots__ = ("_config", "_rules")
44
+
45
+ def __init__(
46
+ self,
47
+ config: Optional[dict] = None,
48
+ rules: Optional[dict] = None,
49
+ ):
50
+ if not config:
51
+ config = DEFAULT_CONFIG
52
+ if not rules:
53
+ rules = NO_RULES
54
+
55
+ self._config = Config(config)
56
+ self._rules = rules
57
+
58
+ def check(self, expected, actual):
59
+ e = self.prepare(expected)
60
+ a = self.prepare(actual)
61
+ diff = self._diff(e, a)
62
+ self.report(diff)
63
+ return diff
64
+
65
+ def _diff(self, e, a):
66
+ t = type(e)
67
+ if not isinstance(a, t):
68
+ return TypesNotEqual(e, a).explain()
69
+ if t is int:
70
+ return self._int_diff(e, a)
71
+ if t is str:
72
+ return self._str_diff(e, a)
73
+ if t is bool:
74
+ return self._bool_diff(e, a)
75
+ if t is float:
76
+ return self._float_diff(e, a)
77
+ if t is dict:
78
+ return self._dict_diff(e, a)
79
+ if t is list:
80
+ return self._list_diff(e, a)
81
+ return NO_DIFF
82
+
83
+ @classmethod
84
+ def _int_diff(cls, e, a):
85
+ if a == e:
86
+ return NO_DIFF
87
+ return ValuesNotEqual(e, a).explain()
88
+
89
+ @classmethod
90
+ def _bool_diff(cls, e, a):
91
+ if a is e:
92
+ return NO_DIFF
93
+ return ValuesNotEqual(e, a).explain()
94
+
95
+ @classmethod
96
+ def _str_diff(cls, e, a):
97
+ if a == e:
98
+ return NO_DIFF
99
+ return ValuesNotEqual(e, a).explain()
100
+
101
+ def _float_diff(self, e, a):
102
+ if a == e:
103
+ return NO_DIFF
104
+ if self._can_rounded_float():
105
+ p = self._float_precision()
106
+ e, a = round(e, p), round(a, p)
107
+ if a == e:
108
+ return NO_DIFF
109
+ return ValuesNotEqual(e, a).explain()
110
+
111
+ def _can_rounded_float(self):
112
+ p = self._float_precision()
113
+ return type(p) is int
114
+
115
+ def _float_precision(self):
116
+ path = 'types.float.allow_round'
117
+ return self._config.get(path)
118
+
119
+ def _dict_diff(self, e, a):
120
+ if not isinstance(a, dict):
121
+ d = {'type' : {'_message': 'Incompatible types'}}
122
+ else:
123
+ d = {}
124
+ for k in e:
125
+ if k not in a:
126
+ d[k] = KeyNotExist(k, None).explain()
127
+ else:
128
+ d[k] = self._diff(e[k], a[k])
129
+
130
+ for k in a:
131
+ if k not in e:
132
+ d[k] = UnexpectedKey(None, k).explain()
133
+ else:
134
+ d[k] = self._diff(e[k], a[k])
135
+
136
+ diffs = self._without_empties(d)
137
+ if diffs:
138
+ name = e.get('name')
139
+ if name:
140
+ diffs['#name'] = name
141
+ return diffs
142
+
143
+ def _list_diff(self, e, a):
144
+ if not isinstance(a, list):
145
+ d = {'type' : {'_message': 'Incompatible types'}}
146
+ else:
147
+ d = {}
148
+ if self._need_compare_length():
149
+ d['_length'] = self._list_len_diff(e, a)
150
+ d['_content'] = self._list_content_diff(e, a)
151
+ return self._without_empties(d)
152
+
153
+ def _need_compare_length(self):
154
+ path = 'types.list.check_length'
155
+ return self._config.get(path) is True
156
+
157
+ def _list_content_diff(self, e, a):
158
+ d = {}
159
+ len_a = len(a)
160
+ for i, v in enumerate(e):
161
+ if i < len_a:
162
+ t = type(v)
163
+ if t in (int, str, bool, float):
164
+ if v != a[i]:
165
+ d[i] = ValuesNotEqual(v, a[i]).explain()
166
+ elif t is dict:
167
+ d[i] = self._dict_diff(v, a[i])
168
+ elif t is list:
169
+ d[i] = self._list_diff(v, a[i])
170
+ return self._without_empties(d)
171
+
172
+ @classmethod
173
+ def _max_diff(cls, e, lst, method):
174
+ t = type(e)
175
+ d = method(e, t())
176
+ for i, v in enumerate(lst):
177
+ if type(v) is t:
178
+ dd = method(e, v)
179
+ if len(dd) <= len(d):
180
+ d = dd
181
+ return d
182
+
183
+ @classmethod
184
+ def _min_diff(cls, e, lst, method):
185
+ t = type(e)
186
+ d = method(e, t())
187
+ for i, v in enumerate(lst):
188
+ if type(v) is t:
189
+ dd = method(e, v)
190
+ if len(dd) <= len(d):
191
+ d = dd
192
+ break
193
+ return d
194
+
195
+ @classmethod
196
+ def _list_len_diff(cls, e, a):
197
+ e, a = len(e), len(a)
198
+ if e == a:
199
+ return NO_DIFF
200
+ return LengthsNotEqual(e, a).explain()
201
+
202
+ @classmethod
203
+ def _without_empties(cls, d):
204
+ return {k: d[k] for k in d if d[k] != NO_DIFF}
205
+
206
+ def report(self, diff):
207
+ if self._need_write_to_console():
208
+ self._write_to_console(diff)
209
+ if self._need_write_to_file():
210
+ self._write_to_file(diff)
211
+
212
+ @classmethod
213
+ def _write_to_console(cls, d):
214
+ msg = json.dumps(d, indent=4)
215
+ print(msg)
216
+
217
+ def _write_to_file(self, d):
218
+ config = self._config.get('output.file')
219
+ with open(config.pop('name'), 'w') as fp:
220
+ json.dump(d, fp, **config)
221
+
222
+ def _need_write_to_console(self):
223
+ path = 'output.console'
224
+ return self._config.get(path) is True
225
+
226
+ def _need_write_to_file(self):
227
+ path = 'output.file.name'
228
+ file_name = self._config.get(path)
229
+ return type(file_name) is str
230
+
231
+ def prepare(self, x):
232
+ x = copy.deepcopy(x)
233
+ return Ignore.transform(x, self._rules)
@@ -0,0 +1,15 @@
1
+ class Config:
2
+ def __init__(self, config: dict):
3
+ self.config = config
4
+
5
+ def get(self, path):
6
+ value = self.config
7
+ for key in path.split('.'):
8
+ try:
9
+ value = value.get(key, {})
10
+ except AttributeError:
11
+ return False
12
+ return value
13
+
14
+ def merge(self, config):
15
+ self.config.update(config)
@@ -0,0 +1,53 @@
1
+ from abc import ABC
2
+
3
+
4
+ class Error(ABC):
5
+ expected = None
6
+ received = None
7
+
8
+ template = 'Expected: <{e}>, received: <{r}>'
9
+
10
+ def __init__(self, expected, received):
11
+ self.expected = expected
12
+ self.received = received
13
+
14
+ @property
15
+ def message(self):
16
+ msg = self.template.format(e=self.expected, r=self.received)
17
+ return msg
18
+
19
+ def explain(self):
20
+ return {
21
+ '_message': self.message,
22
+ '_expected': self.expected,
23
+ '_received': self.received,
24
+ }
25
+
26
+
27
+ class TypesNotEqual(Error):
28
+ template = 'Types not equal. Expected: <{e}>, received: <{r}>'
29
+
30
+ def __init__(self, e, a):
31
+ e = type(e).__name__
32
+ a = type(a).__name__
33
+ super().__init__(e, a)
34
+
35
+
36
+ class ValuesNotEqual(Error):
37
+ template = 'Values not equal. Expected: <{e}>, received: <{r}>'
38
+
39
+
40
+ class KeyNotExist(Error):
41
+ template = 'Key does not exist. Expected: <{e}>'
42
+
43
+
44
+ class LengthsNotEqual(Error):
45
+ template = 'Lengths not equal. Expected <{e}>, received: <{r}>'
46
+
47
+
48
+ class ValueNotFound(Error):
49
+ template = 'Value not found. Expected <{e}>'
50
+
51
+
52
+ class UnexpectedKey(Error):
53
+ template = 'Unexpected key. Received: <{r}>'
@@ -0,0 +1,91 @@
1
+ import re
2
+ from abc import ABC
3
+
4
+
5
+ class Ignore(ABC):
6
+
7
+ @classmethod
8
+ def transform(cls, obj, rules):
9
+ t = type(rules)
10
+ if obj:
11
+ if t is dict:
12
+ return cls._apply_dictable_rule(obj, rules)
13
+ if t is list:
14
+ return cls._apply_listable_rule(obj, rules)
15
+ return obj
16
+
17
+ @classmethod
18
+ def _apply_dictable_rule(cls, obj, rules):
19
+ for key in rules:
20
+ rule = rules[key]
21
+ if cls._is_special_key(key):
22
+ obj = cls._apply_special_rule(key, obj, rule)
23
+ elif cls._is_regex_rule(rule):
24
+ obj = cls._apply_regex_rule(key, obj, rule)
25
+ elif type(rule) is str:
26
+ obj = cls._apply_stringable_rule(key, obj, rule)
27
+ elif key in obj:
28
+ obj[key] = cls.transform(obj[key], rule)
29
+ return obj
30
+
31
+ @classmethod
32
+ def _apply_listable_rule(cls, obj, rules):
33
+ for key in rules:
34
+ if type(key) is dict:
35
+ for index, y in enumerate(obj):
36
+ obj[index] = cls.transform(obj[index], key)
37
+ elif key in obj:
38
+ del obj[key]
39
+ return obj
40
+
41
+ @classmethod
42
+ def _apply_stringable_rule(cls, key, obj, rule):
43
+ if rule == '*':
44
+ if key in obj:
45
+ del obj[key]
46
+ return obj
47
+
48
+ @classmethod
49
+ def _is_regex_rule(cls, rule):
50
+ return type(rule) is dict and '_re' in rule
51
+
52
+ @classmethod
53
+ def _apply_regex_rule(cls, key, obj, rule):
54
+ regex = rule['_re']
55
+ if key in obj and re.match(regex, obj[key]):
56
+ del obj[key]
57
+ return obj
58
+
59
+ @classmethod
60
+ def _is_special_key(cls, key):
61
+ return key.startswith('_')
62
+
63
+ @classmethod
64
+ def _apply_special_rule(cls, key, obj, rule):
65
+ if key == '_values':
66
+ return cls._ignore_values(obj, rule)
67
+ if key == '_list':
68
+ return cls._ignore_list_items(obj, rule)
69
+ if key == '_range':
70
+ return cls._ignore_range(obj, rule)
71
+ return obj
72
+
73
+ @classmethod
74
+ def _ignore_list_items(cls, obj, rule):
75
+ return [cls.transform(x, rule) for x in obj]
76
+
77
+ @classmethod
78
+ def _ignore_values(cls, obj, black_list):
79
+ t = type(obj)
80
+ if t is list:
81
+ return [x for x in obj if x not in black_list]
82
+ if t is dict:
83
+ return {k: obj[k] for k in obj if k not in black_list}
84
+ return obj
85
+
86
+ @classmethod
87
+ def _ignore_range(cls, obj, rule):
88
+ t = type(obj)
89
+ if t is int or t is float:
90
+ return rule[0] <= obj and obj <= rule[1]
91
+ return obj
unisi/proxy.py CHANGED
@@ -40,7 +40,7 @@ class Proxy:
40
40
 
41
41
  def close(self):
42
42
  self.conn.close()
43
-
43
+
44
44
  @property
45
45
  def screen_menu(self):
46
46
  return [name_icon[0] for name_icon in self.screen['menu']] if self.screen else []
@@ -66,7 +66,7 @@ class Proxy:
66
66
  if el == element:
67
67
  return block
68
68
 
69
- def message(self, element, value, event = 'changed'):
69
+ def message(self, element, value = None, event = 'changed'):
70
70
  if event != 'changed' and event not in element:
71
71
  return None
72
72
  return ArgObject(block = self.block_of(element), element = element['name'],
unisi/reloader.py CHANGED
@@ -17,7 +17,7 @@ if config.hot_reload:
17
17
  from watchdog.events import PatternMatchingEventHandler
18
18
  from .users import User
19
19
  from .utils import divpath, Redesign, app_dir
20
- from .autotest import check_screen
20
+ from .autotest import check_module
21
21
  import re, collections
22
22
 
23
23
  #for removing message duplicates
@@ -48,7 +48,7 @@ if config.hot_reload:
48
48
 
49
49
  try:
50
50
  module = user.load_screen(sname)
51
- errors = check_screen(module)
51
+ errors = check_module(module)
52
52
  if errors:
53
53
  print('\n'.join(errors))
54
54
  busy = False
unisi/users.py CHANGED
@@ -175,7 +175,7 @@ class User:
175
175
 
176
176
  def process(self, message):
177
177
  self.last_message = message
178
- screen_change_message = message.screen and self.screen.name != message.screen
178
+ screen_change_message = getattr(message, 'screen',None) and self.screen.name != message.screen
179
179
  if is_screen_switch(message) or screen_change_message:
180
180
  for s in self.screens:
181
181
  if s.name == message.value:
@@ -1,13 +1,12 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: unisi
3
- Version: 0.1.1
3
+ Version: 0.1.3
4
4
  Summary: UNified System Interface, GUI and proxy
5
5
  Author-Email: UNISI Tech <g.dernovoy@gmail.com>
6
6
  License: Apache-2.0
7
7
  Project-URL: Homepage, https://github.com/unisi-tech/unisi
8
8
  Requires-Python: >=3.8
9
9
  Requires-Dist: aiohttp
10
- Requires-Dist: jsoncomparison
11
10
  Requires-Dist: jsonpickle
12
11
  Requires-Dist: pandas
13
12
  Requires-Dist: requests
@@ -29,8 +28,9 @@ UNISI technology provides a unified system interface and advanced program functi
29
28
  - Hot reloading and updating
30
29
  - Integral autotesting
31
30
  - Protocol schema auto validation
31
+ - Shared sessions
32
32
  - Voice interaction (not released yet)
33
- - Developing data pipelines (not released yet)
33
+ - Remote GUI interaction and pipelining (not released yet)
34
34
 
35
35
  ### Installing ###
36
36
  ```
@@ -87,7 +87,7 @@ unisi.start('Test app')
87
87
  Unisi builds the interactive app for the code above.
88
88
  Connect a browser to localhast:8000 which are by default and will see:
89
89
 
90
- ![image](https://github.com/unisi-tech/unisi/assets/1247062/be828e3a-00f8-41ad-9e97-82c01fd566e6)
90
+ ![image](https://github.com/unisi-tech/unisi/assets/1247062/dafebd1f-ae48-4790-9282-dea83d986749)
91
91
 
92
92
  ### Handling events ###
93
93
  All handlers are functions which have a signature
@@ -112,7 +112,7 @@ clean_button = Button('Clean the table’, clean_table)
112
112
  | Gui object | Object to update |
113
113
  | Gui object array or tuple | Objects to update |
114
114
  | None | Nothing to update, Ok |
115
- | Error(...), Warning(...), Info(...) | Show to user info about a problem. |
115
+ | Error(...), Warning(...), Info(...) | Show to user info about a state. |
116
116
  | UpdateScreen, True | Redraw whole screen |
117
117
  | Dialog(..) | Open a dialog with parameters |
118
118
  | user.set_screen(screen_name) | switch to another screen |
@@ -125,12 +125,11 @@ If a Gui object doesn't have 'changed' handler the object accepts incoming value
125
125
  If 'value' is not acceptable instead of returning an object possible to return Error or Warning or Info. That functions can update a object list passed after the message argument.
126
126
 
127
127
  ```
128
- def changed_range(_, value):
129
- if value < 0.5 and value > 1.0:
130
- #or Error(message, _) if we want to return the previous visible value to the field, return gui object _ also.
131
- return Error(f‘The value of {_.name} has to be > 0.5 and < 1.0!', _)
128
+ def changed_range(guirange, value):
129
+ if value < 0.5 and value > 1.0:
130
+ return Error(f‘The value of {guirange.name} has to be > 0.5 and < 1.0!', guirange)
132
131
  #accept value othewise
133
- _.value = value
132
+ guirange.value = value
134
133
 
135
134
  edit = Edit('Range of involving', 0.6, changed_range, type = 'number')
136
135
  ```
@@ -1,16 +1,21 @@
1
- unisi-0.1.1.dist-info/METADATA,sha256=jF4HA-7YYP-mScrb5gIIWNQlB_ecsGzDt2-JglzUseQ,18622
2
- unisi-0.1.1.dist-info/WHEEL,sha256=N2J68yzZqJh3mI_Wg92rwhw0rtJDFpZj9bwQIMJgaVg,90
3
- unisi-0.1.1.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
1
+ unisi-0.1.3.dist-info/METADATA,sha256=pYm__IDa4WqrhosAznrglBGaXw1Ryd-zIF821ajKGrc,18539
2
+ unisi-0.1.3.dist-info/WHEEL,sha256=N2J68yzZqJh3mI_Wg92rwhw0rtJDFpZj9bwQIMJgaVg,90
3
+ unisi-0.1.3.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
4
4
  unisi/__init__.py,sha256=bQ7yTDcgybkbIGTjxkajTbBJXtgyryrCvt3nXg8QxlQ,175
5
- unisi/autotest.py,sha256=mqf6FPPewadwQenG_rgZhhLZEhvZ0H_kLGH1570aLho,8053
5
+ unisi/autotest.py,sha256=K_ev2UcUdMwt6YwK-ueOPavX6ikHylldIF81v05e95o,9397
6
6
  unisi/common.py,sha256=oQJdUHVIf3QEMlnRSY_FW-ce4oDb7xcmzOhHByKGC7w,753
7
7
  unisi/containers.py,sha256=_oB1KknoHAslpmVHY_dpX4uOcKtP5Pelqdv9fMjotHo,3403
8
8
  unisi/guielements.py,sha256=SVfAU9zrGMdzuU3Ip0dAnaPyJSmwoOaozn3LLNW0tkQ,5384
9
- unisi/proxy.py,sha256=YENWammF8xSAgHKUSQJqLVFXhnwdZOGA_sAPSqqEfa8,5728
10
- unisi/reloader.py,sha256=X4bxZn0P6Ey7mj4SAs8rz4RjEtpXd8vny1aGbyYnnik,6424
9
+ unisi/jsoncomparison/__init__.py,sha256=lsWkYEuL6v3Qol-lwSUvB6y63tm6AKcCMUd4DZDx3Cg,350
10
+ unisi/jsoncomparison/compare.py,sha256=XlJqeNaj_zmiD-x_Rz_9R2RYkDd7JCOzojoFtIsrBVU,6158
11
+ unisi/jsoncomparison/config.py,sha256=LbdLJE1KIebFq_tX7zcERhPvopKhnzcTqMCnS3jN124,381
12
+ unisi/jsoncomparison/errors.py,sha256=wqphE1Xn7K6n16uvUhDC45m2BxbsMUhIF2olPbhqf4o,1192
13
+ unisi/jsoncomparison/ignore.py,sha256=xfF0a_BBEyGdZBoq-ovpCpawgcX8SRwwp7IrGnu1c2w,2634
14
+ unisi/proxy.py,sha256=bbuuOitToR-hmy5wQ6sqnJChze0ZuqKr1eHWyhGWXIY,5739
15
+ unisi/reloader.py,sha256=MkmN6mDclrdzuO5I4Hu_GGjOyl-icnJ-IdwyNW8xEYs,6424
11
16
  unisi/server.py,sha256=SKN38DjuxZ0U_FjBJDuwnpDP2YfZorpIJrjrADhqf5w,4521
12
17
  unisi/tables.py,sha256=v7Fio5iIN7u1t7cE4Yvl4ewn7jTmmNPyWigoKW1Mj8U,4239
13
- unisi/users.py,sha256=OFfNrPfYPrBeyziWOO0gB6UlX8vG2VpglrMdY1-_Mi4,9805
18
+ unisi/users.py,sha256=B3Ouj-Ygx9BYU64JtE_HM4yApIZ7ikVeH2v86Lr1rSA,9819
14
19
  unisi/utils.py,sha256=KnyjouXGnN8ubuwogMa1tu2HJSkmDqhuFf7RpU3n9bY,2971
15
20
  unisi/web/css/815.30ec41a2.css,sha256=b8La7chOFF3VPd-kqAbwP-Cx8jMzkgZCbYsMXR2mhKI,2723
16
21
  unisi/web/css/app.31d6cfe0.css,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -34,4 +39,4 @@ unisi/web/js/430.591e9a73.js,sha256=7S1CJTwGdE2EKXzeFRcaYuTrn0QteoVI03Hykz8qh3s,
34
39
  unisi/web/js/815.e46226ff.js,sha256=xpfhA0GvPjvracUep66rqrKLCho-Muiw1N8nr9QzDNc,55923
35
40
  unisi/web/js/app.29d640d6.js,sha256=v_u_Q9vvmrgTj7-4CdpB8yQOl19a-FC6YVCXnSyTfec,5923
36
41
  unisi/web/js/vendor.d6797c01.js,sha256=2aKM3Lfxc0pHSirrcUReL1LcvDYWnfeBj2c67XsSELk,1279477
37
- unisi-0.1.1.dist-info/RECORD,,
42
+ unisi-0.1.3.dist-info/RECORD,,
File without changes