kongalib 2.0.5__cp314-cp314-macosx_10_15_universal2.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.
- _kongalib.cpython-314-darwin.so +0 -0
- kongalib/__init__.py +394 -0
- kongalib/async_client.py +813 -0
- kongalib/client.py +1045 -0
- kongalib/constants.json +1 -0
- kongalib/constants.py +187 -0
- kongalib/data_dictionary.py +203 -0
- kongalib/db.py +267 -0
- kongalib/expression.py +841 -0
- kongalib/json.py +114 -0
- kongalib/lex.py +1058 -0
- kongalib/scripting.py +766 -0
- kongalib/yacc.py +3276 -0
- kongalib-2.0.5.dist-info/METADATA +150 -0
- kongalib-2.0.5.dist-info/RECORD +21 -0
- kongalib-2.0.5.dist-info/WHEEL +6 -0
- kongalib-2.0.5.dist-info/licenses/LICENSE +165 -0
- kongalib-2.0.5.dist-info/top_level.txt +4 -0
- kongalib-2.0.5.dist-info/zip-safe +1 -0
- kongaui.py +507 -0
- kongautil.py +581 -0
kongalib/scripting.py
ADDED
|
@@ -0,0 +1,766 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# _ _ _ _
|
|
3
|
+
# | | | (_) |
|
|
4
|
+
# | | _____ _ __ __ _ __ _| |_| |__
|
|
5
|
+
# | |/ / _ \| '_ \ / _` |/ _` | | | '_ \
|
|
6
|
+
# | < (_) | | | | (_| | (_| | | | |_) |
|
|
7
|
+
# |_|\_\___/|_| |_|\__, |\__,_|_|_|_.__/
|
|
8
|
+
# __/ |
|
|
9
|
+
# |___/
|
|
10
|
+
#
|
|
11
|
+
# Konga client library, by EasyByte Software
|
|
12
|
+
#
|
|
13
|
+
# https://github.com/easybyte-software/kongalib
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
from __future__ import print_function
|
|
17
|
+
from __future__ import absolute_import
|
|
18
|
+
|
|
19
|
+
from kongalib import Error
|
|
20
|
+
from _kongalib import get_application_log_path, set_interpreter_timeout, get_interpreter_timeout, get_interpreter_time_left, _set_process_foreground
|
|
21
|
+
|
|
22
|
+
import sys
|
|
23
|
+
import os
|
|
24
|
+
import atexit
|
|
25
|
+
import io
|
|
26
|
+
import threading
|
|
27
|
+
import multiprocessing
|
|
28
|
+
import multiprocessing.connection
|
|
29
|
+
import asyncio
|
|
30
|
+
import signal
|
|
31
|
+
import logging
|
|
32
|
+
import logging.handlers
|
|
33
|
+
import time
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
DEBUG = False
|
|
37
|
+
|
|
38
|
+
gConnFamily = None
|
|
39
|
+
gConnFamilyOverride = False
|
|
40
|
+
|
|
41
|
+
_DLL_PATHS = []
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class BadConnection(Exception):
|
|
45
|
+
def __init__(self):
|
|
46
|
+
self._bad_connection = True
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class InterpreterTimeout(Exception):
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class InterpreterError(Exception):
|
|
55
|
+
def __init__(self, exc_info):
|
|
56
|
+
self._exc_info = exc_info
|
|
57
|
+
def get_exc_info(self):
|
|
58
|
+
return self._exc_info
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def debug_log(text):
|
|
63
|
+
try:
|
|
64
|
+
_logger.debug(text)
|
|
65
|
+
except:
|
|
66
|
+
try:
|
|
67
|
+
sys.__stderr__.write('%s\n' % text)
|
|
68
|
+
except:
|
|
69
|
+
pass
|
|
70
|
+
# sys.__stderr__.write(text + '\n')
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class _TimeoutBlocker(object):
|
|
75
|
+
def __init__(self):
|
|
76
|
+
self.timeout = 0
|
|
77
|
+
self.lock = threading.RLock()
|
|
78
|
+
def __enter__(self):
|
|
79
|
+
try:
|
|
80
|
+
self.timeout = get_interpreter_time_left() or 0
|
|
81
|
+
set_interpreter_timeout(0)
|
|
82
|
+
except:
|
|
83
|
+
self.timeout = 0
|
|
84
|
+
self.lock.acquire()
|
|
85
|
+
def __exit__(self, exc_type, exc_value, exc_traceback):
|
|
86
|
+
self.lock.release()
|
|
87
|
+
if _State.restore_timeout:
|
|
88
|
+
try:
|
|
89
|
+
set_interpreter_timeout(self.timeout)
|
|
90
|
+
except:
|
|
91
|
+
pass
|
|
92
|
+
else:
|
|
93
|
+
_State.restore_timeout = True
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class Proxy(object):
|
|
97
|
+
def __init__(self):
|
|
98
|
+
self._conn = None
|
|
99
|
+
self._lock = _TimeoutBlocker()
|
|
100
|
+
|
|
101
|
+
def _initialize(self):
|
|
102
|
+
conn_type = str(sys.argv.pop(1))
|
|
103
|
+
address = str(sys.argv.pop(1))
|
|
104
|
+
if conn_type == 'AF_INET':
|
|
105
|
+
colon = address.rfind(':')
|
|
106
|
+
address = (address[:colon], int(address[colon+1:]))
|
|
107
|
+
debug_log("[Proxy] init: %s" % repr(address))
|
|
108
|
+
try:
|
|
109
|
+
self._conn = multiprocessing.connection.Client(address, conn_type)
|
|
110
|
+
except:
|
|
111
|
+
import traceback
|
|
112
|
+
_logger.error("[Proxy] init error: %s" % traceback.format_exc())
|
|
113
|
+
raise
|
|
114
|
+
debug_log("[Proxy] connection established")
|
|
115
|
+
|
|
116
|
+
def is_valid(self):
|
|
117
|
+
return self._conn is not None
|
|
118
|
+
|
|
119
|
+
def close(self):
|
|
120
|
+
if self._conn is not None:
|
|
121
|
+
sys.stdout.flush()
|
|
122
|
+
sys.stderr.flush()
|
|
123
|
+
self._conn.close()
|
|
124
|
+
self._conn = None
|
|
125
|
+
debug_log("[Proxy] connection closed")
|
|
126
|
+
|
|
127
|
+
def __getattr__(self, name):
|
|
128
|
+
return _MethodHandler(self._conn, self._lock, name)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
class _State(object):
|
|
132
|
+
handler = None
|
|
133
|
+
controller = None
|
|
134
|
+
io = []
|
|
135
|
+
restore_timeout = True
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
proxy = Proxy()
|
|
139
|
+
_logger = logging.getLogger("script")
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
def timeout_handler():
|
|
143
|
+
if proxy.builtin.handle_timeout():
|
|
144
|
+
raise InterpreterTimeout
|
|
145
|
+
else:
|
|
146
|
+
timeout = _State.controller.timeout
|
|
147
|
+
set_interpreter_timeout(timeout)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def init_interpreter():
|
|
152
|
+
_State.io.append((sys.stdout, sys.stderr, sys.stdin))
|
|
153
|
+
try:
|
|
154
|
+
proxy._initialize()
|
|
155
|
+
sys.stdout = _ProxyStdOut()
|
|
156
|
+
sys.stderr = _ProxyStdErr()
|
|
157
|
+
# sys.stdout = io.TextIOWrapper(_ProxyStdOut(), 'utf-8', line_buffering=True)
|
|
158
|
+
# sys.stderr = io.TextIOWrapper(_ProxyStdErr(), 'utf-8', line_buffering=True)
|
|
159
|
+
sys.stdin = _ProxyStdIn()
|
|
160
|
+
sys.prefix, sys.exec_prefix = proxy.builtin.get_prefixes()
|
|
161
|
+
sys.is_kongalib_interpreter = True
|
|
162
|
+
except:
|
|
163
|
+
raise BadConnection()
|
|
164
|
+
|
|
165
|
+
import getpass
|
|
166
|
+
getpass.getpass = proxy.builtin.getpass
|
|
167
|
+
|
|
168
|
+
def excepthook(type, value, tb):
|
|
169
|
+
import traceback
|
|
170
|
+
# debug_log('EXCEPTHOOK:\n%s' % '\n'.join(traceback.format_exception(type, value, tb)))
|
|
171
|
+
tb = traceback.extract_tb(tb)
|
|
172
|
+
def do_filter(entry):
|
|
173
|
+
filename = entry[0].replace('\\', '/')
|
|
174
|
+
if filename.endswith('kongalib/scripting.py'):
|
|
175
|
+
return False
|
|
176
|
+
return True
|
|
177
|
+
tb = list(filter(do_filter, tb))
|
|
178
|
+
try:
|
|
179
|
+
proxy.builtin.print_exception(type, value, tb)
|
|
180
|
+
except:
|
|
181
|
+
debug_log('proxy.builtin.print_exception exception:\n%s' % traceback.format_exc())
|
|
182
|
+
sys.excepthook = excepthook
|
|
183
|
+
|
|
184
|
+
def close_proxy():
|
|
185
|
+
try:
|
|
186
|
+
proxy.close()
|
|
187
|
+
except:
|
|
188
|
+
pass
|
|
189
|
+
atexit.register(close_proxy)
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def exit_interpreter():
|
|
193
|
+
sys.stdout, sys.stderr, sys.stdin = _State.io.pop()
|
|
194
|
+
# proxy.close()
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
class _Controller(threading.Thread):
|
|
198
|
+
QUIT_REQUEST = (None, None, None, None, None)
|
|
199
|
+
|
|
200
|
+
def __init__(self, conn, sem):
|
|
201
|
+
self.conn = conn
|
|
202
|
+
self.sem = sem
|
|
203
|
+
self.lock = threading.RLock()
|
|
204
|
+
self.request_cond = threading.Condition(self.lock)
|
|
205
|
+
self.request = None
|
|
206
|
+
self.exc_info = None
|
|
207
|
+
self.timeout = None
|
|
208
|
+
def terminate(signo=None, stack_frame=None):
|
|
209
|
+
self.execute(*_Controller.QUIT_REQUEST)
|
|
210
|
+
signal.signal(signal.SIGTERM, terminate)
|
|
211
|
+
super(_Controller, self).__init__()
|
|
212
|
+
|
|
213
|
+
def get_execute_request(self):
|
|
214
|
+
with self.lock:
|
|
215
|
+
while not self.request_cond.wait(0.5):
|
|
216
|
+
if self.request is not None:
|
|
217
|
+
break
|
|
218
|
+
request = self.request
|
|
219
|
+
self.request = None
|
|
220
|
+
return request
|
|
221
|
+
|
|
222
|
+
def run(self):
|
|
223
|
+
name = None
|
|
224
|
+
while name != 'exit':
|
|
225
|
+
try:
|
|
226
|
+
ready = multiprocessing.connection.wait([ self.conn ], 0.5)
|
|
227
|
+
if not ready:
|
|
228
|
+
if self.request == _Controller.QUIT_REQUEST:
|
|
229
|
+
break
|
|
230
|
+
continue
|
|
231
|
+
data = self.conn.recv()
|
|
232
|
+
handler, name, args, kwargs = data
|
|
233
|
+
msg = repr(args)
|
|
234
|
+
if len(msg) > 80:
|
|
235
|
+
msg = msg[:80] + '[...]'
|
|
236
|
+
except IOError:
|
|
237
|
+
return
|
|
238
|
+
except EOFError:
|
|
239
|
+
return
|
|
240
|
+
except KeyboardInterrupt:
|
|
241
|
+
return
|
|
242
|
+
result = getattr(self, name)(*args, **kwargs)
|
|
243
|
+
try:
|
|
244
|
+
self.conn.send((None, result))
|
|
245
|
+
except IOError:
|
|
246
|
+
return
|
|
247
|
+
except EOFError:
|
|
248
|
+
return
|
|
249
|
+
except KeyboardInterrupt:
|
|
250
|
+
return
|
|
251
|
+
except:
|
|
252
|
+
import traceback
|
|
253
|
+
_logger.debug(traceback.format_exc())
|
|
254
|
+
sys.exit(0)
|
|
255
|
+
|
|
256
|
+
def set_timeout(self, timeout, restore=True):
|
|
257
|
+
_State.restore_timeout = restore
|
|
258
|
+
self.timeout = timeout
|
|
259
|
+
return set_interpreter_timeout(timeout)
|
|
260
|
+
|
|
261
|
+
def get_time_left(self):
|
|
262
|
+
return get_interpreter_time_left() or 0
|
|
263
|
+
|
|
264
|
+
def execute(self, args, path, timeout, script, cwd):
|
|
265
|
+
with self.lock:
|
|
266
|
+
self.request = (args, path, timeout, script, cwd)
|
|
267
|
+
self.request_cond.notify()
|
|
268
|
+
|
|
269
|
+
def set_exc_info(self, exc_info):
|
|
270
|
+
with self.lock:
|
|
271
|
+
self.exc_info = exc_info
|
|
272
|
+
|
|
273
|
+
def get_exc_info(self):
|
|
274
|
+
with self.lock:
|
|
275
|
+
return self.exc_info
|
|
276
|
+
|
|
277
|
+
def exit(self):
|
|
278
|
+
self.sem.release()
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
def _trampoline(conn, sem, foreground, env, dll_paths, queue, level):
|
|
283
|
+
logging.getLogger().setLevel(level)
|
|
284
|
+
logger = logging.getLogger('script._trampoline')
|
|
285
|
+
handler = logging.handlers.QueueHandler(queue)
|
|
286
|
+
handler.setLevel(level)
|
|
287
|
+
logger.addHandler(handler)
|
|
288
|
+
logger.debug('entering interpreter process')
|
|
289
|
+
try:
|
|
290
|
+
_set_process_foreground(foreground)
|
|
291
|
+
for path in dll_paths:
|
|
292
|
+
try:
|
|
293
|
+
os.add_dll_directory(path)
|
|
294
|
+
logger.debug('added DLL directory: %s' % path)
|
|
295
|
+
except:
|
|
296
|
+
if sys.platform == 'win32':
|
|
297
|
+
logger.error('error adding DLL directory: %s' % path)
|
|
298
|
+
for key, value in (env or {}).items():
|
|
299
|
+
key = str(key)
|
|
300
|
+
value = str(value)
|
|
301
|
+
os.environ[key] = value
|
|
302
|
+
logger.debug('added env variable %s=%s' % (key, value))
|
|
303
|
+
|
|
304
|
+
_State.controller = _Controller(conn, sem)
|
|
305
|
+
_State.controller.start()
|
|
306
|
+
|
|
307
|
+
while True:
|
|
308
|
+
request = _State.controller.get_execute_request()
|
|
309
|
+
if request == _Controller.QUIT_REQUEST:
|
|
310
|
+
break
|
|
311
|
+
args, path, timeout, script, cwd = request
|
|
312
|
+
sys.argv = args
|
|
313
|
+
sys.path = path
|
|
314
|
+
filename = args[0]
|
|
315
|
+
if cwd:
|
|
316
|
+
os.chdir(cwd)
|
|
317
|
+
init_interpreter()
|
|
318
|
+
try:
|
|
319
|
+
script = compile(script, filename, 'exec', dont_inherit=1)
|
|
320
|
+
exc = None
|
|
321
|
+
_State.controller.set_timeout(timeout)
|
|
322
|
+
exec(script, { '__file__': filename, '__name__': '__main__' })
|
|
323
|
+
except Exception as e:
|
|
324
|
+
import traceback
|
|
325
|
+
exc_type, exc_value, exc_tb = sys.exc_info()
|
|
326
|
+
exc_tb = traceback.extract_tb(exc_tb)
|
|
327
|
+
exc_info = ( exc_type, exc_value, exc_tb )
|
|
328
|
+
else:
|
|
329
|
+
exc_info = None
|
|
330
|
+
_State.controller.set_timeout(0)
|
|
331
|
+
_State.controller.set_exc_info(exc_info)
|
|
332
|
+
exit_interpreter()
|
|
333
|
+
sem.release()
|
|
334
|
+
try:
|
|
335
|
+
conn.close()
|
|
336
|
+
conn = None
|
|
337
|
+
except:
|
|
338
|
+
pass
|
|
339
|
+
_State.controller.join()
|
|
340
|
+
except KeyboardInterrupt:
|
|
341
|
+
logger.debug('user issued a keyboard interrupt')
|
|
342
|
+
except Exception as e:
|
|
343
|
+
import traceback
|
|
344
|
+
logger.critical('unhandled error in interpreter process: %s' % traceback.format_exc())
|
|
345
|
+
finally:
|
|
346
|
+
if conn is not None:
|
|
347
|
+
try:
|
|
348
|
+
conn.close()
|
|
349
|
+
except:
|
|
350
|
+
pass
|
|
351
|
+
logger.debug('exiting interpreter process')
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
class _ControllerProxy(Proxy):
|
|
355
|
+
class NullLocker(object):
|
|
356
|
+
def __enter__(self):
|
|
357
|
+
pass
|
|
358
|
+
def __exit__(self, exc_type, exc_value, exc_traceback):
|
|
359
|
+
pass
|
|
360
|
+
def __init__(self, conn):
|
|
361
|
+
self._conn = conn
|
|
362
|
+
self._lock = _ControllerProxy.NullLocker()
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
class Interpreter(object):
|
|
366
|
+
def __init__(self, foreground=True, env=None):
|
|
367
|
+
self.proc = None
|
|
368
|
+
self.exc_info = None
|
|
369
|
+
self.conn = None
|
|
370
|
+
self.lock = threading.RLock()
|
|
371
|
+
self.sem = multiprocessing.Semaphore(0)
|
|
372
|
+
self.queue = multiprocessing.Queue()
|
|
373
|
+
self.proxy = None
|
|
374
|
+
self.foreground = foreground
|
|
375
|
+
self.env = env
|
|
376
|
+
self.logger_listener = logging.handlers.QueueListener(self.queue, *logging.getLogger().handlers, respect_handler_level=True)
|
|
377
|
+
|
|
378
|
+
def __del__(self):
|
|
379
|
+
with self.lock:
|
|
380
|
+
if self.proc is not None:
|
|
381
|
+
if self.proc.is_alive():
|
|
382
|
+
try:
|
|
383
|
+
self.proxy.exit()
|
|
384
|
+
except:
|
|
385
|
+
pass
|
|
386
|
+
try:
|
|
387
|
+
self.logger_listener.stop()
|
|
388
|
+
except:
|
|
389
|
+
pass
|
|
390
|
+
|
|
391
|
+
def ensure_proc(self):
|
|
392
|
+
with self.lock:
|
|
393
|
+
if self.proc is None:
|
|
394
|
+
self.conn, self.client_conn = multiprocessing.Pipe()
|
|
395
|
+
self.proxy = _ControllerProxy(self.conn).controller
|
|
396
|
+
self.logger_listener.start()
|
|
397
|
+
self.proc = multiprocessing.Process(target=_trampoline, args=(self.client_conn, self.sem, self.foreground, self.env, _DLL_PATHS, self.queue, logging.getLogger().level), daemon=True)
|
|
398
|
+
self.proc.start()
|
|
399
|
+
exitcode = self.proc.exitcode
|
|
400
|
+
if exitcode is not None:
|
|
401
|
+
raise RuntimeError('Unable to start interpreter process: exit code %d' % exitcode)
|
|
402
|
+
|
|
403
|
+
def execute(self, script=None, filename=None, argv=None, path=None, timeout=None):
|
|
404
|
+
with self.lock:
|
|
405
|
+
self.ensure_proc()
|
|
406
|
+
self.exc_info = None
|
|
407
|
+
args = argv
|
|
408
|
+
if not args:
|
|
409
|
+
args = [ filename or '<script>' ]
|
|
410
|
+
if (script is None) and filename:
|
|
411
|
+
with open(filename, 'r') as f:
|
|
412
|
+
script = f.read()
|
|
413
|
+
try:
|
|
414
|
+
cwd = os.path.dirname(os.path.abspath(filename))
|
|
415
|
+
except:
|
|
416
|
+
cwd = None
|
|
417
|
+
self.proxy.execute(args, path, timeout, script or '', cwd )
|
|
418
|
+
self.lock.release()
|
|
419
|
+
while not self.sem.acquire(False):
|
|
420
|
+
with self.lock:
|
|
421
|
+
if (self.proc is None) or (not self.proc.is_alive()):
|
|
422
|
+
break
|
|
423
|
+
time.sleep(0.05)
|
|
424
|
+
self.lock.acquire()
|
|
425
|
+
if (self.proc is not None) and self.proc.is_alive():
|
|
426
|
+
self.exc_info = self.proxy.get_exc_info()
|
|
427
|
+
if self.exc_info is not None:
|
|
428
|
+
raise InterpreterError(self.exc_info)
|
|
429
|
+
|
|
430
|
+
def stop(self):
|
|
431
|
+
with self.lock:
|
|
432
|
+
proc = self.proc
|
|
433
|
+
if proc is not None:
|
|
434
|
+
self.proc = None
|
|
435
|
+
self.lock.release()
|
|
436
|
+
try:
|
|
437
|
+
try:
|
|
438
|
+
proc.terminate()
|
|
439
|
+
except:
|
|
440
|
+
pass
|
|
441
|
+
proc.join(3)
|
|
442
|
+
if proc.is_alive():
|
|
443
|
+
try:
|
|
444
|
+
proc.kill()
|
|
445
|
+
except Exception as e:
|
|
446
|
+
debug_log('Interpreter.stop() failed with error: %s' % str(e))
|
|
447
|
+
finally:
|
|
448
|
+
self.lock.acquire()
|
|
449
|
+
|
|
450
|
+
def is_running(self):
|
|
451
|
+
conn = self.conn
|
|
452
|
+
proc = self.proc
|
|
453
|
+
return (conn is None) or ((proc is not None) and proc.is_alive())
|
|
454
|
+
|
|
455
|
+
def set_timeout(self, timeout=None, restore=False):
|
|
456
|
+
with self.lock:
|
|
457
|
+
if self.proxy is not None:
|
|
458
|
+
func = self.proxy.set_timeout
|
|
459
|
+
else:
|
|
460
|
+
func = None
|
|
461
|
+
if func is not None:
|
|
462
|
+
return func(timeout, restore)
|
|
463
|
+
|
|
464
|
+
def get_time_left(self):
|
|
465
|
+
with self.lock:
|
|
466
|
+
return self.proxy.get_time_left()
|
|
467
|
+
|
|
468
|
+
def get_exc_info(self):
|
|
469
|
+
with self.lock:
|
|
470
|
+
return self.exc_info
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
class _ProxyStdIn(io.StringIO):
|
|
474
|
+
def readline(self, size=-1):
|
|
475
|
+
return proxy.builtin.read_line()
|
|
476
|
+
|
|
477
|
+
|
|
478
|
+
class _ProxyStdOut(io.StringIO):
|
|
479
|
+
def write(self, text):
|
|
480
|
+
proxy.builtin.write_stdout(str(text))
|
|
481
|
+
return len(text)
|
|
482
|
+
|
|
483
|
+
def flush(self):
|
|
484
|
+
try:
|
|
485
|
+
proxy.builtin.flush_stdout()
|
|
486
|
+
except:
|
|
487
|
+
pass
|
|
488
|
+
|
|
489
|
+
|
|
490
|
+
class _ProxyStdErr(io.StringIO):
|
|
491
|
+
def write(self, text):
|
|
492
|
+
sys.__stderr__.write(str(text))
|
|
493
|
+
try:
|
|
494
|
+
proxy.builtin.write_stderr(str(text))
|
|
495
|
+
except:
|
|
496
|
+
pass
|
|
497
|
+
return len(text)
|
|
498
|
+
|
|
499
|
+
def flush(self):
|
|
500
|
+
try:
|
|
501
|
+
proxy.builtin.flush_stderr()
|
|
502
|
+
except:
|
|
503
|
+
pass
|
|
504
|
+
|
|
505
|
+
|
|
506
|
+
|
|
507
|
+
class _MethodHandler(object):
|
|
508
|
+
def __init__(self, conn, lock, name):
|
|
509
|
+
self._conn = conn
|
|
510
|
+
self._lock = lock
|
|
511
|
+
self._name = name
|
|
512
|
+
|
|
513
|
+
def __getattr__(self, name):
|
|
514
|
+
return _Method(self, name)
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
|
|
518
|
+
class _Method(object):
|
|
519
|
+
def __init__(self, handler, name):
|
|
520
|
+
self.handler = handler
|
|
521
|
+
self.name = name
|
|
522
|
+
|
|
523
|
+
def __call__(self, *args, **kwargs):
|
|
524
|
+
with self.handler._lock:
|
|
525
|
+
if DEBUG:
|
|
526
|
+
s = time.time()
|
|
527
|
+
debug_log('[Proxy] call: %s' % str((self.handler._name, self.name, args, kwargs)))
|
|
528
|
+
self.handler._conn.send((self.handler._name, self.name, args, kwargs))
|
|
529
|
+
if DEBUG:
|
|
530
|
+
debug_log('[Proxy] call sent in %f secs. Waiting reply: %s' % (time.time() - s, str((self.handler._name, self.name))))
|
|
531
|
+
|
|
532
|
+
multiprocessing.connection.wait([ self.handler._conn ])
|
|
533
|
+
e, result = self.handler._conn.recv()
|
|
534
|
+
if DEBUG:
|
|
535
|
+
s = time.time()
|
|
536
|
+
debug_log('[Proxy] got reply in %f secs: %s' % (time.time() - s, str((self.handler._name, self.name, result))))
|
|
537
|
+
if e is None:
|
|
538
|
+
return result
|
|
539
|
+
errmsg, errno = e
|
|
540
|
+
if errno is None:
|
|
541
|
+
raise RuntimeError(errmsg)
|
|
542
|
+
else:
|
|
543
|
+
raise Error(errno, errmsg)
|
|
544
|
+
|
|
545
|
+
|
|
546
|
+
class _ServerProxy(threading.Thread):
|
|
547
|
+
def __init__(self, handlers=None):
|
|
548
|
+
self.handlers = handlers or {}
|
|
549
|
+
self.listener = None
|
|
550
|
+
self.conn = None
|
|
551
|
+
super(_ServerProxy, self).__init__()
|
|
552
|
+
|
|
553
|
+
def start(self):
|
|
554
|
+
if gConnFamilyOverride:
|
|
555
|
+
family = None
|
|
556
|
+
else:
|
|
557
|
+
family = gConnFamily
|
|
558
|
+
try:
|
|
559
|
+
self.listener = multiprocessing.connection.Listener(family=family)
|
|
560
|
+
except:
|
|
561
|
+
raise BadConnection()
|
|
562
|
+
super(_ServerProxy, self).start()
|
|
563
|
+
|
|
564
|
+
def stop(self):
|
|
565
|
+
if self.listener is not None:
|
|
566
|
+
self.listener.close()
|
|
567
|
+
self.listener = None
|
|
568
|
+
if self.conn is not None:
|
|
569
|
+
self.conn.close()
|
|
570
|
+
self.handlers = {}
|
|
571
|
+
if self.is_alive():
|
|
572
|
+
self.join()
|
|
573
|
+
|
|
574
|
+
def run(self):
|
|
575
|
+
debug_log("[ServerProxy] run")
|
|
576
|
+
try:
|
|
577
|
+
self.conn = self.listener.accept()
|
|
578
|
+
debug_log("[ServerProxy] got proxy")
|
|
579
|
+
while True:
|
|
580
|
+
ready = multiprocessing.connection.wait([ self.conn ], 0.5)
|
|
581
|
+
if ready:
|
|
582
|
+
data = self.conn.recv()
|
|
583
|
+
handler, name, args, kwargs = data
|
|
584
|
+
if handler in self.handlers:
|
|
585
|
+
# debug_log("[kongaprint:%s] %s(%s)" % (handler, name, ', '.join([ repr(arg) for arg in args ] + [ '%s=%s' % (key, repr(value)) for key, value in kwargs.iteritems() ])))
|
|
586
|
+
func = getattr(self.handlers[handler], name, None)
|
|
587
|
+
else:
|
|
588
|
+
func = None
|
|
589
|
+
try:
|
|
590
|
+
if func is None:
|
|
591
|
+
raise RuntimeError('Method "%s" unavailable in this context' % name)
|
|
592
|
+
result = func(*args, **kwargs)
|
|
593
|
+
if asyncio.iscoroutine(result):
|
|
594
|
+
try:
|
|
595
|
+
loop = asyncio.get_running_loop()
|
|
596
|
+
except:
|
|
597
|
+
loop = asyncio.get_event_loop_policy().get_event_loop()
|
|
598
|
+
result = asyncio.run_coroutine_threadsafe(result, loop).result()
|
|
599
|
+
result = (None, result)
|
|
600
|
+
except Exception as e:
|
|
601
|
+
import traceback
|
|
602
|
+
_logger.error("[ServerProxy] method error: %s" % traceback.format_exc())
|
|
603
|
+
# sys.__stderr__.write('SCRIPTING EXCEPTION:\n%s\n' % traceback.format_exc())
|
|
604
|
+
if isinstance(e, Error):
|
|
605
|
+
errno = e.errno
|
|
606
|
+
else:
|
|
607
|
+
errno = None
|
|
608
|
+
result = ((str(e), errno), None)
|
|
609
|
+
finally:
|
|
610
|
+
self.conn.send(result)
|
|
611
|
+
except IOError:
|
|
612
|
+
if self.listener is not None:
|
|
613
|
+
import traceback
|
|
614
|
+
debug_log("[ServerProxy] IOError: %s" % traceback.format_exc())
|
|
615
|
+
except EOFError:
|
|
616
|
+
pass
|
|
617
|
+
finally:
|
|
618
|
+
debug_log("[ServerProxy] exiting")
|
|
619
|
+
|
|
620
|
+
|
|
621
|
+
class BuiltinHandler(object):
|
|
622
|
+
def __init__(self):
|
|
623
|
+
self.__interpreter = None
|
|
624
|
+
self.__exit_funcs = []
|
|
625
|
+
|
|
626
|
+
def _set_interpreter(self, interpreter):
|
|
627
|
+
self.__interpreter = interpreter
|
|
628
|
+
|
|
629
|
+
def _get_interpreter(self):
|
|
630
|
+
return self.__interpreter
|
|
631
|
+
|
|
632
|
+
def write_stdout(self, text):
|
|
633
|
+
sys.__stdout__.write(text)
|
|
634
|
+
|
|
635
|
+
def write_stderr(self, text):
|
|
636
|
+
sys.__stderr__.write(text)
|
|
637
|
+
|
|
638
|
+
def flush_stdout(self):
|
|
639
|
+
pass
|
|
640
|
+
|
|
641
|
+
def flush_stderr(self):
|
|
642
|
+
pass
|
|
643
|
+
|
|
644
|
+
def read_line(self):
|
|
645
|
+
sys.__stdin__.readline()
|
|
646
|
+
|
|
647
|
+
def getpass(self, prompt='Password: ', stream=None):
|
|
648
|
+
import getpass
|
|
649
|
+
return getpass.getpass(prompt, stream)
|
|
650
|
+
|
|
651
|
+
def get_prefixes(self):
|
|
652
|
+
return os.getcwd(), os.getcwd()
|
|
653
|
+
|
|
654
|
+
def format_exception(self, type, value, tb):
|
|
655
|
+
import traceback
|
|
656
|
+
if not all([ isinstance(x, str) for x in tb ]):
|
|
657
|
+
tb = traceback.format_list(tb)
|
|
658
|
+
text = [ 'Traceback (most recent call last):\n' ] + tb + traceback.format_exception_only(type, value)
|
|
659
|
+
return ''.join(text)
|
|
660
|
+
|
|
661
|
+
def print_exception(self, type, value, tb):
|
|
662
|
+
print(self.format_exception(type, value, tb))
|
|
663
|
+
|
|
664
|
+
def get_time_left(self):
|
|
665
|
+
if self.__interpreter is not None:
|
|
666
|
+
return self.__interpreter.get_time_left()
|
|
667
|
+
return 0
|
|
668
|
+
|
|
669
|
+
def set_timeout(self, timeout=0, restore=False):
|
|
670
|
+
if self.__interpreter is not None:
|
|
671
|
+
return self.__interpreter.set_timeout(timeout, restore)
|
|
672
|
+
|
|
673
|
+
def handle_timeout(self):
|
|
674
|
+
raise InterpreterTimeout
|
|
675
|
+
|
|
676
|
+
def noop(self):
|
|
677
|
+
pass
|
|
678
|
+
|
|
679
|
+
def atexit(self, func, *args, **kwargs):
|
|
680
|
+
self.__exit_funcs.append((func, args, kwargs))
|
|
681
|
+
|
|
682
|
+
def _atexit(self):
|
|
683
|
+
while self.__exit_funcs:
|
|
684
|
+
func, args, kwargs = self.__exit_funcs.pop()
|
|
685
|
+
func(*args, **kwargs)
|
|
686
|
+
|
|
687
|
+
|
|
688
|
+
def set_connection_family(family):
|
|
689
|
+
global gConnFamily
|
|
690
|
+
gConnFamily = family
|
|
691
|
+
|
|
692
|
+
|
|
693
|
+
def add_dll_directory(path):
|
|
694
|
+
if path not in _DLL_PATHS:
|
|
695
|
+
_DLL_PATHS.append(path)
|
|
696
|
+
|
|
697
|
+
|
|
698
|
+
def execute(script=None, filename=None, argv=None, path=None, timeout=0, handlers=None, interpreter=None):
|
|
699
|
+
import traceback
|
|
700
|
+
if (script is None) and (filename is None):
|
|
701
|
+
raise ValueError('Either script or filename must be specified')
|
|
702
|
+
debug_log("[ServerProxy] launching...")
|
|
703
|
+
if filename is None:
|
|
704
|
+
filename = '<script>'
|
|
705
|
+
if argv is None:
|
|
706
|
+
argv = [ filename ]
|
|
707
|
+
if interpreter is None:
|
|
708
|
+
interpreter = Interpreter()
|
|
709
|
+
debug_log("[ServerProxy] instantiating ServerProxy")
|
|
710
|
+
_handlers = { 'builtin': BuiltinHandler() }
|
|
711
|
+
_handlers.update(handlers or {})
|
|
712
|
+
_handlers['builtin']._set_interpreter(interpreter)
|
|
713
|
+
while True:
|
|
714
|
+
p = _ServerProxy(_handlers)
|
|
715
|
+
try:
|
|
716
|
+
p.start()
|
|
717
|
+
debug_log("[ServerProxy] listener address is: %s" % repr(p.listener.address))
|
|
718
|
+
conn_type = multiprocessing.connection.address_type(p.listener.address)
|
|
719
|
+
if conn_type == 'AF_INET':
|
|
720
|
+
address = '%s:%d' % tuple(p.listener.address)
|
|
721
|
+
else:
|
|
722
|
+
address = p.listener.address
|
|
723
|
+
argv.insert(1, conn_type)
|
|
724
|
+
argv.insert(2, address)
|
|
725
|
+
debug_log("[ServerProxy] waiting proxy: %s" % repr(argv))
|
|
726
|
+
|
|
727
|
+
# import time
|
|
728
|
+
# start = time.time()
|
|
729
|
+
interpreter.execute(script, filename, argv, path or [], timeout)
|
|
730
|
+
# print("Script execution time:", time.time() - start)
|
|
731
|
+
except Exception as e:
|
|
732
|
+
if getattr(e, '_bad_connection', False) and (gConnFamily is not None):
|
|
733
|
+
debug_log("[ServerProxy] bad connection, trying default connection family")
|
|
734
|
+
global gConnFamilyOverride
|
|
735
|
+
gConnFamilyOverride = True
|
|
736
|
+
set_connection_family(None)
|
|
737
|
+
argv[1:3] = []
|
|
738
|
+
continue
|
|
739
|
+
if isinstance(e, InterpreterError):
|
|
740
|
+
type, value, tb = e.get_exc_info()
|
|
741
|
+
else:
|
|
742
|
+
debug_log("[ServerProxy] unhandled execute exception: %s" % traceback.format_exc())
|
|
743
|
+
type, value, tb = sys.exc_info()
|
|
744
|
+
tb = traceback.format_tb(tb)
|
|
745
|
+
def do_filter(entry):
|
|
746
|
+
filename = entry[0].replace('\\', '/')
|
|
747
|
+
if filename.endswith('kongalib/scripting.py') or filename.endswith('__script_host__.py'):
|
|
748
|
+
return False
|
|
749
|
+
return True
|
|
750
|
+
tb = list(filter(do_filter, tb))
|
|
751
|
+
try:
|
|
752
|
+
_handlers['builtin'].print_exception(type, value, tb)
|
|
753
|
+
except:
|
|
754
|
+
debug_log('proxy.builtin.print_exception exception:\n%s' % traceback.format_exc())
|
|
755
|
+
finally:
|
|
756
|
+
_handlers['builtin']._set_interpreter(None)
|
|
757
|
+
interpreter = None
|
|
758
|
+
_handlers['builtin']._atexit()
|
|
759
|
+
try:
|
|
760
|
+
debug_log("[ServerProxy] done")
|
|
761
|
+
finally:
|
|
762
|
+
p.stop()
|
|
763
|
+
break
|
|
764
|
+
|
|
765
|
+
|
|
766
|
+
|