klongpy 0.6.9__py3-none-any.whl → 0.7.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.
- klongpy/__init__.py +17 -1
- klongpy/adverbs.py +84 -82
- klongpy/autograd.py +299 -0
- klongpy/backend.py +38 -103
- klongpy/backends/__init__.py +26 -0
- klongpy/backends/base.py +469 -0
- klongpy/backends/numpy_backend.py +123 -0
- klongpy/backends/registry.py +76 -0
- klongpy/backends/torch_backend.py +1047 -0
- klongpy-0.6.9.data/scripts/kgpy → klongpy/cli.py +110 -90
- klongpy/core.py +113 -974
- klongpy/db/sys_fn_db.py +7 -6
- klongpy/db/sys_fn_kvs.py +2 -4
- klongpy/dyads.py +332 -160
- klongpy/interpreter.py +60 -15
- klongpy/monads.py +121 -75
- klongpy/parser.py +328 -0
- klongpy/repl.py +23 -5
- klongpy/sys_fn.py +170 -21
- klongpy/sys_fn_autograd.py +290 -0
- klongpy/sys_fn_ipc.py +22 -15
- klongpy/sys_fn_timer.py +13 -3
- klongpy/types.py +503 -0
- klongpy/web/sys_fn_web.py +14 -4
- klongpy/writer.py +122 -0
- klongpy/ws/sys_fn_ws.py +5 -8
- klongpy-0.7.1.dist-info/METADATA +544 -0
- klongpy-0.7.1.dist-info/RECORD +52 -0
- {klongpy-0.6.9.dist-info → klongpy-0.7.1.dist-info}/WHEEL +1 -1
- klongpy-0.7.1.dist-info/entry_points.txt +2 -0
- {klongpy-0.6.9.dist-info → klongpy-0.7.1.dist-info}/top_level.txt +0 -1
- klongpy-0.6.9.dist-info/METADATA +0 -448
- klongpy-0.6.9.dist-info/RECORD +0 -77
- tests/__init__.py +0 -6
- tests/gen_join_over.py +0 -119
- tests/gen_py_suite.py +0 -77
- tests/gen_test_fn.py +0 -259
- tests/perf_async.py +0 -25
- tests/perf_avg.py +0 -18
- tests/perf_duckdb.py +0 -32
- tests/perf_gen.py +0 -38
- tests/perf_ipc_overhead.py +0 -34
- tests/perf_join.py +0 -53
- tests/perf_load.py +0 -17
- tests/perf_prog.py +0 -18
- tests/perf_serdes.py +0 -52
- tests/perf_sys_fn_db.py +0 -263
- tests/perf_vector.py +0 -40
- tests/test_accel.py +0 -227
- tests/test_df_cache.py +0 -85
- tests/test_eval_monad_list.py +0 -34
- tests/test_examples.py +0 -64
- tests/test_extra_suite.py +0 -382
- tests/test_file_cache.py +0 -185
- tests/test_interop.py +0 -180
- tests/test_kg_asarray.py +0 -94
- tests/test_kgtests.py +0 -65
- tests/test_known_bugs.py +0 -206
- tests/test_prog.py +0 -107
- tests/test_reshape_strings.py +0 -33
- tests/test_suite.py +0 -1480
- tests/test_suite_file.py +0 -153
- tests/test_sys_fn.py +0 -420
- tests/test_sys_fn_db.py +0 -88
- tests/test_sys_fn_ipc.py +0 -587
- tests/test_sys_fn_timer.py +0 -133
- tests/test_sys_fn_web.py +0 -50
- tests/test_util.py +0 -233
- tests/utils.py +0 -126
- {klongpy-0.6.9.dist-info → klongpy-0.7.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,27 +1,29 @@
|
|
|
1
|
-
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
KlongPy REPL command-line interface.
|
|
4
|
+
|
|
5
|
+
See https://t3x.org/klong/klong-ref.txt.html for additional details.
|
|
6
|
+
"""
|
|
2
7
|
|
|
3
8
|
import argparse
|
|
4
9
|
import asyncio
|
|
10
|
+
import importlib
|
|
11
|
+
import importlib.util
|
|
5
12
|
import importlib.metadata
|
|
6
13
|
import os
|
|
7
14
|
import sys
|
|
8
15
|
import threading
|
|
9
|
-
import time
|
|
10
16
|
import timeit
|
|
11
17
|
|
|
12
18
|
import colorama
|
|
13
19
|
|
|
14
|
-
from klongpy import KlongInterpreter
|
|
20
|
+
from klongpy import KlongInterpreter, list_backends
|
|
15
21
|
from klongpy.core import kg_write
|
|
16
|
-
from klongpy.
|
|
17
|
-
|
|
18
|
-
import importlib.resources
|
|
19
|
-
|
|
20
|
-
"""
|
|
22
|
+
from klongpy.repl import cleanup_repl, create_repl
|
|
21
23
|
|
|
22
|
-
|
|
24
|
+
_READLINE_SPEC = importlib.util.find_spec("readline")
|
|
25
|
+
readline = importlib.import_module("readline") if _READLINE_SPEC else None
|
|
23
26
|
|
|
24
|
-
"""
|
|
25
27
|
|
|
26
28
|
def sys_cmd_shell(klong, cmd):
|
|
27
29
|
"""
|
|
@@ -112,7 +114,7 @@ def sys_cmd_exit(klong, cmd):
|
|
|
112
114
|
|
|
113
115
|
"""
|
|
114
116
|
print("bye!")
|
|
115
|
-
|
|
117
|
+
return ExitRequest(0)
|
|
116
118
|
|
|
117
119
|
|
|
118
120
|
def sys_cmd_transcript(klong, cmd):
|
|
@@ -174,10 +176,17 @@ success = lambda input: f"{colorama.Fore.GREEN}{input}"
|
|
|
174
176
|
failure = lambda input: f"{colorama.Fore.RED}{input}"
|
|
175
177
|
|
|
176
178
|
|
|
179
|
+
class ExitRequest:
|
|
180
|
+
def __init__(self, code=0):
|
|
181
|
+
self.code = 0 if code is None else code
|
|
182
|
+
|
|
183
|
+
|
|
177
184
|
async def repl_eval(klong, p, verbose=True):
|
|
178
185
|
try:
|
|
179
186
|
r = klong(p)
|
|
180
|
-
r = r if r is None else success(kg_write(r, display=False))
|
|
187
|
+
r = r if r is None else success(kg_write(r, klong._backend, display=False))
|
|
188
|
+
except SystemExit as exc:
|
|
189
|
+
return ExitRequest(exc.code)
|
|
181
190
|
except Exception as e:
|
|
182
191
|
r = failure(f"Error: {e.args}")
|
|
183
192
|
if verbose:
|
|
@@ -187,12 +196,14 @@ async def repl_eval(klong, p, verbose=True):
|
|
|
187
196
|
return r
|
|
188
197
|
|
|
189
198
|
|
|
190
|
-
def show_repl_header(ipc_addr=None):
|
|
199
|
+
def show_repl_header(backend_name, device_name=None, ipc_addr=None):
|
|
191
200
|
print()
|
|
192
201
|
print(f"{colorama.Fore.GREEN}Welcome to KlongPy REPL v{importlib.metadata.distribution('klongpy').version}")
|
|
193
202
|
print(f"{colorama.Fore.GREEN}Author: Brian Guarraci")
|
|
194
203
|
print(f"{colorama.Fore.GREEN}Web: http://klongpy.org")
|
|
195
|
-
|
|
204
|
+
device_str = f" ({device_name})" if device_name else ""
|
|
205
|
+
print(f"{colorama.Fore.CYAN}Backend: {backend_name}{device_str}")
|
|
206
|
+
print(f"{colorama.Fore.YELLOW}]h for help; Ctrl-D or ]q to quit")
|
|
196
207
|
print()
|
|
197
208
|
if ipc_addr:
|
|
198
209
|
print(f"{colorama.Fore.RED}Running IPC server at {ipc_addr}")
|
|
@@ -209,7 +220,7 @@ def run_in_loop(klong_loop, coro):
|
|
|
209
220
|
|
|
210
221
|
class ConsoleInputHandler:
|
|
211
222
|
@staticmethod
|
|
212
|
-
async def input_producer(console_loop, klong_loop, klong, verbose=False):
|
|
223
|
+
async def input_producer(console_loop, klong_loop, klong, verbose=False, exit_state=None):
|
|
213
224
|
sys_cmds = create_sys_cmd_functions()
|
|
214
225
|
try:
|
|
215
226
|
while True:
|
|
@@ -225,13 +236,29 @@ class ConsoleInputHandler:
|
|
|
225
236
|
continue
|
|
226
237
|
else:
|
|
227
238
|
r = run_in_loop(klong_loop, repl_eval(klong, s, verbose=verbose))
|
|
239
|
+
if isinstance(r, ExitRequest):
|
|
240
|
+
if exit_state is not None:
|
|
241
|
+
exit_state["code"] = r.code
|
|
242
|
+
break
|
|
228
243
|
if r is not None:
|
|
229
244
|
print(r)
|
|
230
245
|
except EOFError:
|
|
231
246
|
print("\rbye!")
|
|
232
247
|
break
|
|
233
248
|
except KeyboardInterrupt:
|
|
234
|
-
|
|
249
|
+
buf = ""
|
|
250
|
+
if readline is not None:
|
|
251
|
+
try:
|
|
252
|
+
buf = readline.get_line_buffer()
|
|
253
|
+
except Exception:
|
|
254
|
+
buf = ""
|
|
255
|
+
if buf:
|
|
256
|
+
print()
|
|
257
|
+
continue
|
|
258
|
+
print("\rbye!")
|
|
259
|
+
if exit_state is not None:
|
|
260
|
+
exit_state["code"] = 130
|
|
261
|
+
break
|
|
235
262
|
except Exception as e:
|
|
236
263
|
print(failure(f"Error: {e.args}"))
|
|
237
264
|
import traceback
|
|
@@ -241,65 +268,24 @@ class ConsoleInputHandler:
|
|
|
241
268
|
|
|
242
269
|
|
|
243
270
|
async def run_in_klong(klong, s):
|
|
244
|
-
|
|
271
|
+
try:
|
|
272
|
+
return klong(s)
|
|
273
|
+
except SystemExit as exc:
|
|
274
|
+
return ExitRequest(exc.code)
|
|
245
275
|
|
|
246
276
|
|
|
247
277
|
def run_file(klong_loop, klong, fname, verbose=False):
|
|
278
|
+
# Add script directory to sys.path so .py/.pyf imports resolve sibling modules
|
|
279
|
+
script_dir = os.path.dirname(os.path.abspath(fname))
|
|
280
|
+
if script_dir not in sys.path:
|
|
281
|
+
sys.path.insert(0, script_dir)
|
|
248
282
|
with open(fname, "r") as f:
|
|
249
283
|
content = f.read()
|
|
250
|
-
run_in_loop(klong_loop, run_in_klong(klong, content))
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
def start_loop(loop, stop_event):
|
|
254
|
-
asyncio.set_event_loop(loop)
|
|
255
|
-
loop.run_until_complete(stop_event.wait())
|
|
284
|
+
return run_in_loop(klong_loop, run_in_klong(klong, content))
|
|
256
285
|
|
|
257
286
|
|
|
258
|
-
def
|
|
259
|
-
|
|
260
|
-
loop.slow_callback_duration = slow_callback_duration
|
|
261
|
-
if debug:
|
|
262
|
-
loop.set_debug(True)
|
|
263
|
-
stop_event = asyncio.Event()
|
|
264
|
-
thread = threading.Thread(target=lambda l=loop,e=stop_event: start_loop_func(l,e), args=(loop,), daemon=True)
|
|
265
|
-
thread.start()
|
|
266
|
-
return loop, thread, stop_event
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
def cleanup_async_loop(loop: asyncio.AbstractEventLoop, loop_thread, stop_event, debug=True, name=None) -> None:
|
|
270
|
-
|
|
271
|
-
if loop.is_closed():
|
|
272
|
-
return
|
|
273
|
-
|
|
274
|
-
loop.call_soon_threadsafe(stop_event.set)
|
|
275
|
-
loop_thread.join()
|
|
276
|
-
|
|
277
|
-
pending_tasks = asyncio.all_tasks(loop=loop)
|
|
278
|
-
if len(pending_tasks) > 0:
|
|
279
|
-
if name:
|
|
280
|
-
print(f"WARNING: pending tasks in {name} loop")
|
|
281
|
-
print(f"cancelling {len(pending_tasks)} pending tasks...")
|
|
282
|
-
for task in pending_tasks:
|
|
283
|
-
loop.call_soon_threadsafe(task.cancel)
|
|
284
|
-
# wait for all tasks to be cancelled but we can't use run_until_complete because the loop is already running
|
|
285
|
-
while len(asyncio.all_tasks(loop=loop)) > 0:
|
|
286
|
-
time.sleep(0)
|
|
287
|
-
|
|
288
|
-
loop.stop()
|
|
289
|
-
|
|
290
|
-
if not loop.is_closed():
|
|
291
|
-
loop.close()
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
def append_pkg_resource_path_KLONGPATH():
|
|
295
|
-
with importlib.resources.as_file(importlib.resources.files('klongpy')) as pkg_path:
|
|
296
|
-
pkg_lib_path = os.path.join(pkg_path, "lib")
|
|
297
|
-
klongpath = os.environ.get("KLONGPATH", ".:lib")
|
|
298
|
-
klongpath = f"{klongpath}:{pkg_lib_path}" if klongpath else str(pkg_lib_path)
|
|
299
|
-
os.environ["KLONGPATH"] = klongpath
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
if __name__ == "__main__":
|
|
287
|
+
def main():
|
|
288
|
+
"""Main entry point for the KlongPy REPL."""
|
|
303
289
|
if '--' in sys.argv:
|
|
304
290
|
index = sys.argv.index('--')
|
|
305
291
|
main_args = sys.argv[:index]
|
|
@@ -318,37 +304,49 @@ if __name__ == "__main__":
|
|
|
318
304
|
parser.add_argument('-t', '--test', help='test program from file')
|
|
319
305
|
parser.add_argument('-v', '--verbose', help='enable verbose output', action="store_true")
|
|
320
306
|
parser.add_argument('-d', '--debug', help='enable debug mode', action="store_true")
|
|
307
|
+
parser.add_argument('--backend', help='set array backend', type=str.lower, choices=list_backends())
|
|
308
|
+
parser.add_argument('--device', help='set backend device (torch only)', type=str)
|
|
321
309
|
parser.add_argument('filename', nargs='?', help='filename to be run if no flags are specified')
|
|
322
310
|
|
|
323
311
|
args = parser.parse_args(main_args[1:])
|
|
324
312
|
|
|
313
|
+
# Default to torch backend if available and not explicitly set
|
|
314
|
+
if args.backend is None:
|
|
315
|
+
available_backends = list_backends()
|
|
316
|
+
if 'torch' in available_backends:
|
|
317
|
+
args.backend = 'torch'
|
|
318
|
+
|
|
325
319
|
if args.debug:
|
|
326
320
|
print("args: ", args)
|
|
327
321
|
|
|
328
322
|
if args.expr:
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
shutdown_event = CallbackEvent()
|
|
323
|
+
try:
|
|
324
|
+
klong = KlongInterpreter(backend=args.backend, device=args.device)
|
|
325
|
+
except ValueError as exc:
|
|
326
|
+
parser.error(str(exc))
|
|
327
|
+
result = klong(args.expr)
|
|
328
|
+
if result is not None:
|
|
329
|
+
print(kg_write(result, klong._backend, display=False))
|
|
330
|
+
return
|
|
338
331
|
|
|
339
|
-
|
|
332
|
+
try:
|
|
333
|
+
klong, loops = create_repl(debug=args.debug, backend=args.backend, device=args.device)
|
|
334
|
+
except ValueError as exc:
|
|
335
|
+
parser.error(str(exc))
|
|
336
|
+
io_loop, _, _, klong_loop, _, _ = loops
|
|
337
|
+
shutdown_event = klong['.system']['closeEvent']
|
|
340
338
|
|
|
341
339
|
klong['.helpdb'] = []
|
|
342
|
-
klong['.system'] = {'ioloop': io_loop, 'klongloop': klong_loop, 'closeEvent': shutdown_event}
|
|
343
340
|
klong['.os.env'] = dict(os.environ)
|
|
344
341
|
klong['.os.argv'] = extras if extras else []
|
|
345
342
|
|
|
346
343
|
run_repl = False
|
|
344
|
+
exit_code = None
|
|
347
345
|
|
|
348
346
|
if args.server:
|
|
349
347
|
r = klong(f".srv({args.server})")
|
|
350
348
|
if r == 0:
|
|
351
|
-
print(
|
|
349
|
+
print("Failed to start server")
|
|
352
350
|
elif args.test:
|
|
353
351
|
print(f"Test: {args.test}")
|
|
354
352
|
with open(args.test, "r") as f:
|
|
@@ -362,7 +360,9 @@ if __name__ == "__main__":
|
|
|
362
360
|
if args.filename:
|
|
363
361
|
if args.verbose:
|
|
364
362
|
print(f"Running: {args.filename}")
|
|
365
|
-
run_file(klong_loop, klong, args.filename, verbose=args.verbose)
|
|
363
|
+
result = run_file(klong_loop, klong, args.filename, verbose=args.verbose)
|
|
364
|
+
if isinstance(result, ExitRequest):
|
|
365
|
+
exit_code = result.code
|
|
366
366
|
|
|
367
367
|
def gather_io_tasks(io_loop):
|
|
368
368
|
done_event = threading.Event()
|
|
@@ -377,7 +377,8 @@ if __name__ == "__main__":
|
|
|
377
377
|
done_event.set()
|
|
378
378
|
return done_event
|
|
379
379
|
|
|
380
|
-
|
|
380
|
+
if exit_code is None:
|
|
381
|
+
gather_io_tasks(io_loop).wait()
|
|
381
382
|
else:
|
|
382
383
|
run_repl = True
|
|
383
384
|
|
|
@@ -390,14 +391,33 @@ if __name__ == "__main__":
|
|
|
390
391
|
if args.load:
|
|
391
392
|
if args.verbose:
|
|
392
393
|
print(f"Loading: {args.load}")
|
|
393
|
-
run_file(klong_loop, klong, args.load, verbose=args.verbose)
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
394
|
+
result = run_file(klong_loop, klong, args.load, verbose=args.verbose)
|
|
395
|
+
if isinstance(result, ExitRequest):
|
|
396
|
+
exit_code = result.code
|
|
397
|
+
run_repl = False
|
|
398
|
+
if run_repl:
|
|
399
|
+
exit_state = {"code": None}
|
|
400
|
+
colorama.init(autoreset=True)
|
|
401
|
+
backend_name = klong._backend.name
|
|
402
|
+
device_name = str(klong._backend.device) if hasattr(klong._backend, 'device') else None
|
|
403
|
+
show_repl_header(backend_name, device_name, args.server)
|
|
404
|
+
console_loop.create_task(ConsoleInputHandler.input_producer(console_loop, klong_loop, klong, args.verbose, exit_state))
|
|
405
|
+
try:
|
|
406
|
+
console_loop.run_forever()
|
|
407
|
+
except KeyboardInterrupt:
|
|
408
|
+
exit_state["code"] = 130
|
|
409
|
+
console_loop.stop()
|
|
410
|
+
console_loop.close()
|
|
411
|
+
if exit_state["code"] is not None:
|
|
412
|
+
exit_code = exit_state["code"]
|
|
399
413
|
|
|
400
414
|
shutdown_event.trigger()
|
|
401
415
|
|
|
402
|
-
|
|
403
|
-
|
|
416
|
+
cleanup_repl(loops, debug=args.debug)
|
|
417
|
+
|
|
418
|
+
if exit_code is not None:
|
|
419
|
+
sys.exit(exit_code)
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
if __name__ == "__main__":
|
|
423
|
+
main()
|