unisi 0.2.8__py3-none-any.whl → 0.3.1__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/__init__.py CHANGED
@@ -1,7 +1,7 @@
1
1
  from .utils import *
2
2
  from .units import *
3
- from .users import User, handle, context_user, context_screen
4
- from .server import start
3
+ from .users import User
4
+ from .server import start, handle, context_user, context_screen
5
5
  from .tables import *
6
6
  from .containers import *
7
7
  from .proxy import *
unisi/autotest.py CHANGED
@@ -1,3 +1,4 @@
1
+ # Copyright © 2024 UNISI Tech. All rights reserved.
1
2
  import config, os, json, asyncio
2
3
  from .utils import *
3
4
  from .units import *
unisi/common.py CHANGED
@@ -1,3 +1,4 @@
1
+ # Copyright © 2024 UNISI Tech. All rights reserved.
1
2
  import jsonpickle, inspect, asyncio
2
3
 
3
4
  UpdateScreen = True
@@ -42,12 +43,6 @@ class ArgObject:
42
43
  def __getattr__(self, _):
43
44
  """return None for unknown props"""
44
45
  return None
45
-
46
- class ReceivedMessage(ArgObject):
47
- def __init__(self, kwargs):
48
- self.__dict__.update(kwargs)
49
- def __str__(self):
50
- return f'{self.block}/{self.element}->{self.event}({self.value})'
51
46
  @property
52
47
  def screen_type(self):
53
48
  return self.block == 'root' and self.element is None
@@ -55,6 +50,12 @@ class ReceivedMessage(ArgObject):
55
50
  def voice_type(self):
56
51
  return self.block == 'voice' and self.element is None
57
52
 
53
+ class ReceivedMessage(ArgObject):
54
+ def __init__(self, kwargs):
55
+ self.__dict__.update(kwargs)
56
+ def __str__(self):
57
+ return f'{self.block}/{self.element}->{self.event}({self.value})'
58
+
58
59
  def toJson(obj):
59
60
  return jsonpickle.encode(obj,unpicklable = False)
60
61
 
@@ -64,6 +65,8 @@ def set_defaults(self, param_defaults : dict):
64
65
  setattr(self, param, value)
65
66
 
66
67
  def pretty4(name):
68
+ if name.startswith('_'):
69
+ name = name[1:]
67
70
  pretty_name = name.replace('_',' ')
68
71
  return pretty_name[0].upper() + pretty_name[1:]
69
72
 
unisi/containers.py CHANGED
@@ -1,3 +1,4 @@
1
+ # Copyright © 2024 UNISI Tech. All rights reserved.
1
2
  from .units import *
2
3
  from .common import pretty4, flatten, delete_unit
3
4
  from numbers import Number
@@ -50,8 +51,7 @@ class Block(Unit):
50
51
  user = Unishare.context_user()
51
52
  if hasattr(self,'closable'):
52
53
  def close(*_):
53
- delete_unit(user.screen.blocks, self.name)
54
- return Redesign
54
+ delete_unit(user.screen.blocks, self.name)
55
55
  self.close = close
56
56
 
57
57
  self.set_reactivity(user)
@@ -80,6 +80,11 @@ class Block(Unit):
80
80
  self.scaler.changed(self.scaler, sval)
81
81
  self.set_reactivity(Unishare.context_user())
82
82
 
83
+ def find(self, elem: Unit | str):
84
+ for e in flatten(self.value):
85
+ if e == elem or e.name == elem:
86
+ return e
87
+
83
88
  class ParamBlock(Block):
84
89
  def __init__(self, name, *args, row = 3, **params):
85
90
  """ does not need reactivity so Block init is not used"""
@@ -95,26 +100,26 @@ class ParamBlock(Block):
95
100
 
96
101
  for param, val in params.items():
97
102
  pretty_name = pretty4(param)
98
- t = type(val)
99
- if t == str or t == int or t == float:
100
- el = Edit(pretty_name, val)
101
- elif t == bool:
102
- el = Switch(pretty_name, val)
103
- elif t == tuple or t == list:
104
- if len(val) != 2:
105
- raise ValueError('Composite value has to contain the current value and options value!')
106
- options = val[1]
107
- if not isinstance(options, list | tuple | dict):
108
- raise ValueError('Options value (the second parameter) has to be a list or tuple!')
109
- if len(options) == 3 and all(map(lambda e: isinstance(e, Number), options)):
110
- el = Range(pretty_name, val[0], options = options)
111
- elif isinstance(options, list | tuple):
112
- el = Select(pretty_name, val[0], options = options, type = 'select')
113
- else:
114
- el = Tree(pretty_name, val[0], options = options)
115
- else:
116
- raise ValueError(f'The {param} value {val} is not supported. Look at ParamBlock documentation!')
117
-
103
+ match val:
104
+ case True | False:
105
+ el = Switch(pretty_name, val)
106
+ case str() | int() | float():
107
+ el = Edit(pretty_name, val)
108
+ case tuple() | list():
109
+ if len(val) != 2:
110
+ raise ValueError('Composite value has to contain the current value and options value!')
111
+ options = val[1]
112
+ if not isinstance(options, list | tuple | dict):
113
+ raise ValueError('Options value (the second parameter) has to be a list or tuple!')
114
+ if len(options) == 3 and all(map(lambda e: isinstance(e, Number), options)):
115
+ el = Range(pretty_name, val[0], options = options)
116
+ elif isinstance(options, list | tuple):
117
+ el = Select(pretty_name, val[0], options = options, type = 'select')
118
+ else:
119
+ el = Tree(pretty_name, val[0], options = options)
120
+ case _:
121
+ raise ValueError(f'The {param} value {val} is not supported. Look at ParamBlock documentation!')
122
+
118
123
  self.name2elem[param] = el
119
124
 
120
125
  if cnt % row == 0:
@@ -136,9 +141,9 @@ class Dialog:
136
141
  self.icon = icon
137
142
  self.value = [[], *content] if content else []
138
143
 
139
- class Screen:
140
- def __init__(self, name, **kwargs):
141
- self.name = name
142
- self.__dict__.update(kwargs)
143
- self.type = 'screen'
144
+ class Screen(Unit):
145
+ def __init__(self, name):
146
+ self._mark_changed = None
147
+ self.name = name
148
+ self.type = 'screen'
144
149
 
unisi/dbunits.py CHANGED
@@ -1,3 +1,4 @@
1
+ # Copyright © 2024 UNISI Tech. All rights reserved.
1
2
  from .common import Unishare
2
3
  from collections import defaultdict
3
4
 
unisi/graphs.py CHANGED
@@ -1,3 +1,4 @@
1
+ # Copyright © 2024 UNISI Tech. All rights reserved.
1
2
  from .units import *
2
3
  from .tables import Table
3
4
  from .containers import Block
unisi/kdb.py CHANGED
@@ -1,3 +1,4 @@
1
+ # Copyright © 2024 UNISI Tech. All rights reserved.
1
2
  import kuzu, shutil, os, re
2
3
  from datetime import date, datetime
3
4
  from cymple import QueryBuilder as qb
unisi/llmrag.py CHANGED
@@ -1,3 +1,4 @@
1
+ # Copyright © 2024 UNISI Tech. All rights reserved.
1
2
  from .common import Unishare
2
3
  from langchain_groq import ChatGroq
3
4
  from langchain_openai import ChatOpenAI
unisi/multimon.py CHANGED
@@ -1,3 +1,4 @@
1
+ # Copyright © 2024 UNISI Tech. All rights reserved.
1
2
  import multiprocessing, time, asyncio, logging, inspect
2
3
  from .utils import start_logging
3
4
  from config import froze_time, monitor_tick, profile, pool
unisi/proxy.py CHANGED
@@ -1,3 +1,4 @@
1
+ # Copyright © 2024 UNISI Tech. All rights reserved.
1
2
  from websocket import create_connection
2
3
  from enum import IntFlag
3
4
  import json, requests, os
unisi/reloader.py CHANGED
@@ -1,3 +1,4 @@
1
+ # Copyright © 2024 UNISI Tech. All rights reserved.
1
2
  from .autotest import config
2
3
 
3
4
  empty_app = {
unisi/server.py CHANGED
@@ -1,14 +1,75 @@
1
+ # Copyright © 2024 UNISI Tech. All rights reserved.
1
2
  from aiohttp import web, WSMsgType
2
3
  from .users import *
3
4
  from pathlib import Path
4
5
  from .reloader import empty_app
5
6
  from .autotest import recorder, run_tests
6
7
  from .common import *
7
- from.llmrag import setup_llmrag
8
+ from .llmrag import setup_llmrag
8
9
  from .dbunits import dbupdates
10
+ from .kdb import Database
9
11
  from config import port, upload_dir
10
12
  import traceback, json
11
13
 
14
+ def context_user():
15
+ return context_object(User)
16
+
17
+ def context_screen():
18
+ user = context_user()
19
+ return user.screen if user else None
20
+
21
+ def message_logger(str, type = 'error'):
22
+ user = context_user()
23
+ user.log(str, type)
24
+
25
+ Unishare.context_user = context_user
26
+ Unishare.message_logger = message_logger
27
+ User.type = User
28
+
29
+ if config.db_dir:
30
+ Unishare.db = Database(config.db_dir, message_logger)
31
+
32
+ def make_user(request):
33
+ session = f'{request.remote}-{User.count}'
34
+ if requested_connect := request.query_string if config.share else None:
35
+ user = Unishare.sessions.get(requested_connect, None)
36
+ if not user:
37
+ error = f'Session id "{requested_connect}" is unknown. Connection refused!'
38
+ with logging_lock:
39
+ logging.error(error)
40
+ return None, Error(error)
41
+ user = User.type(session, user)
42
+ ok = user.screens
43
+ elif config.mirror and User.count:
44
+ user = User.type(session, User.last_user)
45
+ ok = user.screens
46
+ elif not User.count:
47
+ user = User.last_user
48
+ user.session = session
49
+ user.monitor(session)
50
+ ok = True
51
+ else:
52
+ user = User.type(session)
53
+ ok = user.load()
54
+
55
+ User.count += 1
56
+ Unishare.sessions[session] = user
57
+ return user, ok
58
+
59
+ def handle(elem, event):
60
+ def h(fn):
61
+ key = elem, event
62
+ handler_map = User.last_user.handlers
63
+ func = handler_map.get(key, None)
64
+ if func:
65
+ handler_map[key] = compose_handlers(func, fn)
66
+ else:
67
+ handler_map[key] = fn
68
+ return fn
69
+ return h
70
+
71
+ Unishare.handle = handle
72
+
12
73
  async def post_handler(request):
13
74
  reader = await request.multipart()
14
75
  field = await reader.next()
@@ -21,7 +82,6 @@ async def post_handler(request):
21
82
  break
22
83
  size += len(chunk)
23
84
  f.write(chunk)
24
-
25
85
  return web.Response(text=filename)
26
86
 
27
87
  async def static_serve(request):
@@ -34,14 +94,12 @@ async def static_serve(request):
34
94
  file_path = None
35
95
  #unmask win path
36
96
  if rpath.startswith('/') and rpath[2] == ':':
37
- rpath = rpath[1:]
38
- dirs = getattr(config, public_dirs, [])
39
- for dir in dirs:
97
+ rpath = rpath[1:]
98
+ for dir in config.public_dirs:
40
99
  if rpath.startswith(dir):
41
100
  if os.path.exists(rpath):
42
101
  file_path = Path(rpath)
43
- break
44
-
102
+ break
45
103
  return web.FileResponse(file_path) if file_path else web.HTTPNotFound()
46
104
 
47
105
  async def websocket_handler(request):
unisi/tables.py CHANGED
@@ -1,3 +1,4 @@
1
+ # Copyright © 2024 UNISI Tech. All rights reserved.
1
2
  from .units import Unit
2
3
  from .common import *
3
4
  from .dbunits import Dblist, dbupdates
unisi/units.py CHANGED
@@ -1,3 +1,4 @@
1
+ # Copyright © 2024 UNISI Tech. All rights reserved.
1
2
  from .common import *
2
3
  from .llmrag import get_property
3
4
 
@@ -22,13 +23,26 @@ class ChangedProxy:
22
23
  elif not callable(value) and not isinstance(value, atomics):
23
24
  return ChangedProxy(value, self)
24
25
  return value
26
+
27
+ def __setattr__(self, name, value):
28
+ if name in ('_obj', '_unit'):
29
+ super().__setattr__(name, value)
30
+ else:
31
+ self._obj.__setattr__(name, value)
32
+ self._unit._mark_changed()
25
33
 
26
34
  def __setitem__(self, key, value):
27
35
  self._obj[key] = value
28
36
  self._unit._mark_changed ()
29
37
 
30
38
  def __getitem__(self, key):
31
- return self._obj[key]
39
+ value = self._obj[key]
40
+ if not callable(value) and not isinstance(value, atomics):
41
+ value = ChangedProxy(value, self._unit)
42
+ return value
43
+
44
+ def __eq__(self, other):
45
+ return self._obj.__eq__(other._obj) if isinstance(other, ChangedProxy) else self._obj.__eq__(other)
32
46
 
33
47
  def __delitem__(self, key):
34
48
  del self._obj[key]
@@ -42,6 +56,16 @@ class ChangedProxy:
42
56
  return len(self._obj)
43
57
  except TypeError:
44
58
  return 0
59
+
60
+ def __hash__(self):
61
+ return hash(self._obj)
62
+
63
+ def __iadd__(self, other):
64
+ if isinstance(self._obj, list):
65
+ self.extend(other)
66
+ return self # Important: __iadd__ must return self
67
+
68
+ raise TypeError(f"Unsupported operand type for += with '{type(self._obj).__name__}'")
45
69
 
46
70
  def __getstate__(self):
47
71
  return self._obj
@@ -49,6 +73,7 @@ class ChangedProxy:
49
73
  atomics = (int, float, complex, bool, str, bytes, ChangedProxy, type(None))
50
74
 
51
75
  class Unit:
76
+ action_list = set(['complete', 'update', 'changed','delete','append', 'modify', 'complete'])
52
77
  def __init__(self, name, *args, **kwargs):
53
78
  self._mark_changed = None
54
79
  self.name = name
@@ -81,9 +106,7 @@ class Unit:
81
106
 
82
107
  def __setattr__(self, name, value):
83
108
  #it is correct condition order
84
- if name != "_mark_changed" and self._mark_changed:
85
- if name != "__dict__" and not isinstance(value, atomics) and not callable(value):
86
- value = ChangedProxy(value, self)
109
+ if name[0] != "_" and self._mark_changed:
87
110
  self._mark_changed(name, value)
88
111
  super().__setattr__(name, value)
89
112
 
@@ -103,7 +126,13 @@ class Unit:
103
126
  def delattr(self, attr):
104
127
  if hasattr(self, attr):
105
128
  delattr(self, attr)
106
-
129
+
130
+ def __eq__(self, other):
131
+ return super().__eq__(other._obj) if isinstance(other, ChangedProxy) else super().__eq__(other)
132
+
133
+ def __hash__(self):
134
+ return super().__hash__()
135
+
107
136
  @property
108
137
  def compact_view(self) -> str:
109
138
  """reduce for external (llm) using if required"""
@@ -126,8 +155,8 @@ class Unit:
126
155
  obj.value = value
127
156
  self.changed = compose_handlers(changed_handler, handler)
128
157
 
129
- def __getstate__(self):
130
- return {n: v for n, v in self.__dict__.items() if n[0] != '_'}
158
+ def __getstate__(self):
159
+ return {n: (True if n in Unit.action_list else v) for n, v in self.__dict__.items() if n[0] != '_'}
131
160
 
132
161
  def __str__(self):
133
162
  return f'{type(self).__name__}({self.name})'
@@ -153,6 +182,7 @@ def smart_complete(lst, min_input_length = 0, max_output_length = 20):
153
182
  class Edit(Unit):
154
183
  def __init__(self, name, *args, **kwargs):
155
184
  super().__init__(name, *args, **kwargs)
185
+ self.x = 0
156
186
  has_value = hasattr(self,'value')
157
187
  if 'type' not in kwargs:
158
188
  if has_value:
@@ -263,6 +293,7 @@ class Tree(Unit):
263
293
  class TextArea(Unit):
264
294
  def __init__(self,name, *args, **kwargs):
265
295
  super().__init__(name, *args, **kwargs)
296
+ self.x = 0
266
297
  self.type = 'text'
267
298
 
268
299
 
unisi/users.py CHANGED
@@ -1,10 +1,10 @@
1
+ # Copyright © 2024 UNISI Tech. All rights reserved.
1
2
  from .utils import *
2
3
  from .units import *
3
4
  from .common import *
4
5
  from .voicecom import VoiceCom
5
6
  from .containers import Dialog, Screen
6
7
  from .multimon import notify_monitor, logging_lock, run_external_process
7
- from .kdb import Database
8
8
  from .dbunits import dbshare, dbupdates
9
9
  import sys, asyncio, logging, importlib
10
10
 
@@ -23,7 +23,7 @@ class User:
23
23
  if share:
24
24
  self.screens = share.screens
25
25
  self.screen_module = share.screen_module if share.screens else []
26
- self.__handlers__ = share.__handlers__
26
+ self.handlers = share.handlers
27
27
 
28
28
  if share.reflections:
29
29
  share.reflections.append(self)
@@ -34,7 +34,7 @@ class User:
34
34
  self.screens = []
35
35
  self.reflections = []
36
36
  self.screen_module = None
37
- self.__handlers__ = {}
37
+ self.handlers = {}
38
38
 
39
39
  User.last_user = self
40
40
  self.monitor(session, share)
@@ -79,7 +79,7 @@ class User:
79
79
  module.user = self
80
80
 
81
81
  spec.loader.exec_module(module)
82
- screen = Screen(getattr(module, 'name', ''))
82
+ screen = Screen(getattr(module, 'name', ''))
83
83
  #set system vars
84
84
  for var, val in screen.defaults.items():
85
85
  setattr(screen, var, getattr(module, var, val))
@@ -89,22 +89,23 @@ class User:
89
89
  screen.toolbar += User.toolbar
90
90
  else:
91
91
  screen.toolbar = User.toolbar
92
- module.screen = screen
92
+ screen.set_reactivity(self)
93
+ module.screen = screen#ChangedProxy(screen, screen)
93
94
  return module
94
95
 
95
96
  async def delete(self):
96
97
  uss = Unishare.sessions
97
98
  if uss and uss.get(self.session):
98
- del uss[self.session]
99
-
99
+ del uss[self.session]
100
100
  if self.reflections: #reflections is common array
101
101
  if len(self.reflections) == 2:
102
102
  self.reflections.clear() #1 element in user.reflections has no sense
103
103
  else:
104
104
  self.reflections.remove(self)
105
-
106
105
  if notify_monitor:
107
106
  await notify_monitor('-', self.session, self.last_message)
107
+ if config.share:
108
+ self.log(f'User is disconnected, session: {self.session}', type = 'info')
108
109
 
109
110
  def set_clean(self):
110
111
  #remove user modules from sys
@@ -145,8 +146,8 @@ class User:
145
146
  def screen(self):
146
147
  return self.screen_module.screen
147
148
 
148
- def set_screen(self,name):
149
- return asyncio.run(self.process(ArgObject(block = 'root', element = None, value = name)))
149
+ def set_screen(self,name):
150
+ return self.screen_process(ArgObject(block = 'root', element = None, value = name))
150
151
 
151
152
  async def result4message(self, message):
152
153
  result = None
@@ -217,10 +218,10 @@ class User:
217
218
  return ['toolbar', e.name]
218
219
 
219
220
  def prepare_result(self, raw):
220
- if raw is True or raw == Redesign:
221
- out = self.screen
222
- out.reload = raw == Redesign
223
- raw = out
221
+ reload_screen = self.screen in self.changed_units
222
+ if reload_screen or raw is True or raw == Redesign:
223
+ self.screen.reload = reload_screen or raw == Redesign
224
+ raw = self.screen
224
225
  else:
225
226
  match raw:
226
227
  case None:
@@ -242,8 +243,8 @@ class User:
242
243
 
243
244
  self.changed_units.clear()
244
245
  return raw
245
-
246
- async def process(self, message):
246
+
247
+ def screen_process(self, message):
247
248
  screen_change_message = message.screen and self.screen.name != message.screen
248
249
  if screen_change_message or message.screen_type:
249
250
  for s in self.screens:
@@ -261,17 +262,20 @@ class User:
261
262
  error = f'Unknown screen name: {message.value}'
262
263
  self.log(error)
263
264
  return Error(error)
264
- elif message.voice_type:
265
- created = False
265
+
266
+ async def process(self, message):
267
+ if screen_result := self.screen_process(message):
268
+ return screen_result
269
+ elif message.voice_type:
266
270
  if not self.voice:
267
- self.voice = VoiceCom(self)
268
- created = True
271
+ self.voice = VoiceCom(self)
269
272
  if message.event == 'listen':
270
- return self.voice.start() if message.value else self.voice.stop()
273
+ if message.value:
274
+ self.voice.start()
275
+ else:
276
+ self.voice.stop()
271
277
  else:
272
- self.voice.input_word(message.value)
273
- if created:
274
- return Redesign
278
+ self.voice.process_word(message.value)
275
279
  else:
276
280
  elem = self.find_element(message)
277
281
  if elem:
@@ -283,7 +287,7 @@ class User:
283
287
  async def process_element(self, elem, message):
284
288
  event = message.event
285
289
  query = event == 'complete' or event == 'append' or event == 'get'
286
- handler = self.__handlers__.get((elem, event), None)
290
+ handler = self.handlers.get((elem, event), None)
287
291
  if handler:
288
292
  return await self.eval_handler(handler, elem, message.value)
289
293
 
@@ -358,62 +362,3 @@ class User:
358
362
  sync_calls.append(user.send(update4user))
359
363
  dbupdates.clear()
360
364
  await asyncio.gather(*sync_calls)
361
-
362
- def context_user():
363
- return context_object(User)
364
-
365
- def context_screen():
366
- user = context_user()
367
- return user.screen if user else None
368
-
369
- def message_logger(str, type = 'error'):
370
- user = context_user()
371
- user.log(str, type)
372
-
373
- Unishare.context_user = context_user
374
- Unishare.message_logger = message_logger
375
- User.type = User
376
-
377
- if config.db_dir:
378
- Unishare.db = Database(config.db_dir, message_logger)
379
-
380
- def make_user(request):
381
- session = f'{request.remote}-{User.count}'
382
- if requested_connect := request.query_string if config.share else None:
383
- user = Unishare.sessions.get(requested_connect, None)
384
- if not user:
385
- error = f'Session id "{requested_connect}" is unknown. Connection refused!'
386
- with logging_lock:
387
- logging.error(error)
388
- return None, Error(error)
389
- user = User.type(session, user)
390
- ok = user.screens
391
- elif config.mirror and User.count:
392
- user = User.type(session, User.last_user)
393
- ok = user.screens
394
- elif not User.count:
395
- user = User.last_user
396
- user.session = session
397
- user.monitor(session)
398
- ok = True
399
- else:
400
- user = User.type(session)
401
- ok = user.load()
402
-
403
- User.count += 1
404
- Unishare.sessions[session] = user
405
- return user, ok
406
-
407
- def handle(elem, event):
408
- def h(fn):
409
- key = elem, event
410
- handler_map = User.last_user.__handlers__
411
- func = handler_map.get(key, None)
412
- if func:
413
- handler_map[key] = compose_handlers(func, fn)
414
- else:
415
- handler_map[key] = fn
416
- return fn
417
- return h
418
-
419
- Unishare.handle = handle
unisi/utils.py CHANGED
@@ -1,10 +1,10 @@
1
+ # Copyright © 2024 UNISI Tech. All rights reserved.
1
2
  import os, platform, requests, logging
2
3
  from .common import set_defaults
3
4
  from .containers import Screen
4
5
 
5
6
  blocks_dir = 'blocks'
6
7
  screens_dir = 'screens'
7
- public_dirs = 'public_dirs'
8
8
  testdir = 'autotest'
9
9
 
10
10
  divpath = '\\' if platform.system() == 'Windows' else '/'
@@ -18,7 +18,7 @@ except:
18
18
  f = open('config.py', 'w')
19
19
  f.write("""port = 8000
20
20
  upload_dir = 'web'
21
- hot_reload = True
21
+ hot_reload = True
22
22
  logfile = 'log'
23
23
  autotest = '*'
24
24
  appname = 'Unisi app'
@@ -41,7 +41,9 @@ set_defaults(config, dict(
41
41
  froze_time= None,
42
42
  monitor_tick = 0.005,
43
43
  pool = None,
44
- db_dir = None
44
+ db_dir = None,
45
+ lang = 'en-US',
46
+ public_dirs = []
45
47
  ))
46
48
 
47
49
  Screen.defaults = dict(
@@ -51,7 +53,8 @@ Screen.defaults = dict(
51
53
  header = config.appname,
52
54
  toolbar = [],
53
55
  order = 0,
54
- reload = config.hot_reload
56
+ reload = config.hot_reload,
57
+ lang = config.lang
55
58
  )
56
59
 
57
60
  if config.froze_time == 0: