klongpy 0.6.7__py3-none-any.whl → 0.6.9__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/core.py +16 -7
- klongpy/dyads.py +14 -4
- klongpy/interpreter.py +8 -4
- klongpy/lib/help.kg +2 -2
- klongpy/monads.py +11 -10
- klongpy/repl.py +73 -0
- klongpy/sys_fn.py +3 -3
- klongpy/sys_fn_ipc.py +0 -1
- klongpy/web/sys_fn_web.py +14 -2
- {klongpy-0.6.7.dist-info → klongpy-0.6.9.dist-info}/METADATA +66 -32
- {klongpy-0.6.7.dist-info → klongpy-0.6.9.dist-info}/RECORD +21 -16
- {klongpy-0.6.7.dist-info → klongpy-0.6.9.dist-info}/WHEEL +1 -1
- tests/test_eval_monad_list.py +34 -0
- tests/test_interop.py +0 -1
- tests/test_kg_asarray.py +94 -0
- tests/test_reshape_strings.py +33 -0
- tests/test_suite.py +1 -0
- tests/test_sys_fn_web.py +50 -0
- {klongpy-0.6.7.data → klongpy-0.6.9.data}/scripts/kgpy +0 -0
- {klongpy-0.6.7.dist-info → klongpy-0.6.9.dist-info/licenses}/LICENSE +0 -0
- {klongpy-0.6.7.dist-info → klongpy-0.6.9.dist-info}/top_level.txt +0 -0
klongpy/core.py
CHANGED
|
@@ -87,6 +87,12 @@ class KGCond(list):
|
|
|
87
87
|
pass
|
|
88
88
|
|
|
89
89
|
|
|
90
|
+
def safe_inspect(fn, follow_wrapped=True):
|
|
91
|
+
try:
|
|
92
|
+
return inspect.signature(fn, follow_wrapped=follow_wrapped).parameters
|
|
93
|
+
except ValueError:
|
|
94
|
+
return {"args":[]}
|
|
95
|
+
|
|
90
96
|
class KGLambda:
|
|
91
97
|
"""
|
|
92
98
|
KGLambda wraps a lambda and make it available to Klong, allowing for direct
|
|
@@ -109,7 +115,7 @@ class KGLambda:
|
|
|
109
115
|
"""
|
|
110
116
|
def __init__(self, fn, args=None, provide_klong=False, wildcard=False):
|
|
111
117
|
self.fn = fn
|
|
112
|
-
params = args or
|
|
118
|
+
params = args or safe_inspect(fn)
|
|
113
119
|
self.args = [reserved_fn_symbol_map[x] for x in reserved_fn_args if x in params]
|
|
114
120
|
self._provide_klong = provide_klong or 'klong' in params
|
|
115
121
|
self._wildcard = wildcard
|
|
@@ -253,17 +259,19 @@ def kg_asarray(a):
|
|
|
253
259
|
arr : ndarray
|
|
254
260
|
The converted input data as a NumPy array, where all elements and sub-arrays are also NumPy arrays.
|
|
255
261
|
"""
|
|
262
|
+
if isinstance(a, str):
|
|
263
|
+
return str_to_chr_arr(a)
|
|
256
264
|
try:
|
|
257
265
|
arr = np.asarray(a)
|
|
258
266
|
if arr.dtype.kind not in ['O','i','f']:
|
|
259
267
|
raise ValueError
|
|
260
268
|
except (np.VisibleDeprecationWarning, ValueError):
|
|
261
269
|
try:
|
|
262
|
-
arr = np.asarray(a,dtype=object)
|
|
270
|
+
arr = np.asarray(a, dtype=object)
|
|
263
271
|
except ValueError:
|
|
264
272
|
arr = [x.tolist() if np.isarray(x) else x for x in a]
|
|
265
|
-
arr = np.asarray(arr,dtype=object)
|
|
266
|
-
arr = np.asarray([kg_asarray(x) if isinstance(x,list) else x for x in arr],dtype=object)
|
|
273
|
+
arr = np.asarray(arr, dtype=object)
|
|
274
|
+
arr = np.asarray([kg_asarray(x) if isinstance(x, list) else x for x in arr], dtype=object)
|
|
267
275
|
return arr
|
|
268
276
|
|
|
269
277
|
|
|
@@ -636,6 +644,7 @@ def read_cond(klong, t, i=0):
|
|
|
636
644
|
i = cexpect(t, i, ';')
|
|
637
645
|
i,n = klong._expr(t, i, ignore_newline=True)
|
|
638
646
|
r.append(n)
|
|
647
|
+
i = skip(t,i,ignore_newline=True)
|
|
639
648
|
i = cexpect(t, i, ']')
|
|
640
649
|
return i, KGCond(r)
|
|
641
650
|
|
|
@@ -694,9 +703,9 @@ def kg_read(t, i=0, read_neg=False, ignore_newline=False, module=None, list_leve
|
|
|
694
703
|
if i >= len(t):
|
|
695
704
|
return i, None
|
|
696
705
|
a = t[i]
|
|
697
|
-
if a
|
|
698
|
-
|
|
699
|
-
|
|
706
|
+
if a == '\n':
|
|
707
|
+
a = ';' # convert newlines to semicolons
|
|
708
|
+
if a in [';','(',')','{','}',']']:
|
|
700
709
|
return i+1,a
|
|
701
710
|
elif cmatch2(t, i, '0', 'c'):
|
|
702
711
|
return read_char(t, i)
|
klongpy/dyads.py
CHANGED
|
@@ -181,7 +181,11 @@ def eval_dyad_at_index(klong, a, b):
|
|
|
181
181
|
j = False
|
|
182
182
|
else:
|
|
183
183
|
r = a
|
|
184
|
-
|
|
184
|
+
if j:
|
|
185
|
+
if np.isarray(r) and r.ndim > 1:
|
|
186
|
+
return np.asarray(["".join(x) for x in r], dtype=object)
|
|
187
|
+
return "".join(r)
|
|
188
|
+
return r
|
|
185
189
|
|
|
186
190
|
|
|
187
191
|
def eval_dyad_define(klong, n, v):
|
|
@@ -401,9 +405,11 @@ def _e_dyad_format2(a, b):
|
|
|
401
405
|
"""
|
|
402
406
|
Unravel the broadcasting of a and b and apply __e_dyad_format2
|
|
403
407
|
"""
|
|
408
|
+
if is_list(a) and is_list(b):
|
|
409
|
+
return kg_asarray([vec_fn2(x, y, _e_dyad_format2) for x, y in zip(to_list(a), to_list(b))])
|
|
404
410
|
if np.isarray(a) and np.isarray(b):
|
|
405
|
-
return np.asarray([vec_fn2(x,y,_e_dyad_format2) for x,y in zip(a,b)])
|
|
406
|
-
return __e_dyad_format2(a,b)
|
|
411
|
+
return np.asarray([vec_fn2(x, y, _e_dyad_format2) for x, y in zip(a, b)])
|
|
412
|
+
return __e_dyad_format2(a, b)
|
|
407
413
|
|
|
408
414
|
def eval_dyad_format2(a, b):
|
|
409
415
|
"""
|
|
@@ -832,7 +838,11 @@ def eval_dyad_reshape(a, b):
|
|
|
832
838
|
r = np.concatenate((np.tile(b,ns), b[:a - b.shape[0]*ns[0]]))
|
|
833
839
|
else:
|
|
834
840
|
r = np.full((a,), b)
|
|
835
|
-
|
|
841
|
+
if j:
|
|
842
|
+
if np.isarray(r) and r.ndim > 1:
|
|
843
|
+
return np.asarray(["".join(x) for x in r], dtype=object)
|
|
844
|
+
return "".join(r)
|
|
845
|
+
return r
|
|
836
846
|
|
|
837
847
|
|
|
838
848
|
def eval_dyad_rotate(a, b):
|
klongpy/interpreter.py
CHANGED
|
@@ -119,7 +119,7 @@ class KlongContext():
|
|
|
119
119
|
if dk.startswith(tk):
|
|
120
120
|
return True
|
|
121
121
|
return False
|
|
122
|
-
|
|
122
|
+
|
|
123
123
|
def __iter__(self):
|
|
124
124
|
seen = set()
|
|
125
125
|
for d in self._context:
|
|
@@ -358,6 +358,7 @@ class KlongInterpreter():
|
|
|
358
358
|
if safe_eq(a, '{'): # read fn
|
|
359
359
|
i,a = self.prog(t, i, ignore_newline=True)
|
|
360
360
|
a = a[0] if len(a) == 1 else a
|
|
361
|
+
i = skip(t, i, ignore_newline=True)
|
|
361
362
|
i = cexpect(t, i, '}')
|
|
362
363
|
arity = get_fn_arity(a)
|
|
363
364
|
if cmatch(t, i, '(') or cmatch2(t,i,':','('):
|
|
@@ -416,6 +417,7 @@ class KlongInterpreter():
|
|
|
416
417
|
if safe_eq(aa, '{'): # read fn
|
|
417
418
|
i,aa = self.prog(t, i, ignore_newline=True)
|
|
418
419
|
aa = aa[0] if len(aa) == 1 else aa
|
|
420
|
+
i = skip(t, i, ignore_newline=True)
|
|
419
421
|
i = cexpect(t, i, '}')
|
|
420
422
|
arity = get_fn_arity(aa)
|
|
421
423
|
if cmatch(t, i, '(') or cmatch2(t,i,':','('):
|
|
@@ -433,6 +435,8 @@ class KlongInterpreter():
|
|
|
433
435
|
i, aaa = self._expr(t, i, ignore_newline=ignore_newline)
|
|
434
436
|
a = KGFn(aa, [a, aaa], arity=2)
|
|
435
437
|
ii, aa = kg_read(t, i, ignore_newline=ignore_newline, module=self.current_module())
|
|
438
|
+
if ignore_newline and safe_eq(a, '\n'):
|
|
439
|
+
i = skip(t, i, ignore_newline=True)
|
|
436
440
|
return i, a
|
|
437
441
|
|
|
438
442
|
def prog(self, t, i=0, ignore_newline=False):
|
|
@@ -481,12 +485,12 @@ class KlongInterpreter():
|
|
|
481
485
|
- f_args is the list of arguments provided to the function during the current invocation.
|
|
482
486
|
- f.args (if f is an instance of KGFn) represents predefined arguments associated with the function.
|
|
483
487
|
These are arguments that are already set by virtue of the function being a projection of another function.
|
|
484
|
-
- During the resolution process, if the function f is a KGFn with predefined arguments (projections),
|
|
488
|
+
- During the resolution process, if the function f is a KGFn with predefined arguments (projections),
|
|
485
489
|
these arguments are appended to the f_args list.
|
|
486
|
-
- The resolution process ensures that if there are placeholders in the predefined arguments, they are
|
|
490
|
+
- The resolution process ensures that if there are placeholders in the predefined arguments, they are
|
|
487
491
|
filled in with values from the provided arguments. However, if the function itself is being projected,
|
|
488
492
|
f_args can still contain a None indicating an empty projection slot.
|
|
489
|
-
- By the end of the resolution, f_args contains the arguments that will be passed to the function for
|
|
493
|
+
- By the end of the resolution, f_args contains the arguments that will be passed to the function for
|
|
490
494
|
evaluation. It is possible for f_args to finally contain None if the function is itself being projected.
|
|
491
495
|
- If `f.a` (the main function or symbol) resolves to another function `_f`, it might indicate a higher-order function scenario.
|
|
492
496
|
- In such a case, `f_args` supplies a function as an argument to `f.a`.
|
klongpy/lib/help.kg
CHANGED
|
@@ -30,8 +30,8 @@ op.db::[
|
|
|
30
30
|
[" a:$b" "Form"
|
|
31
31
|
"b=string; convert 'b' to an object of the same form (type) as 'a'"]
|
|
32
32
|
[" $a" "Format" "convert 'a' to a string representing the value of 'a'"]
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
[" a$b" "Format2"
|
|
34
|
+
"a=real|list; when both operands are lists, apply pairwise. Format 'b', pad with 'a' blanks or to align to x.y digits"]
|
|
35
35
|
[" >a" "Grade-Down"
|
|
36
36
|
"a=vector; vector of indices of elements of 'a' in ascending order"]
|
|
37
37
|
[" <a" "Grade-Up"
|
klongpy/monads.py
CHANGED
|
@@ -74,7 +74,8 @@ def eval_monad_expand_where(a):
|
|
|
74
74
|
&[0 1 0 1 0] --> [1 3]
|
|
75
75
|
|
|
76
76
|
"""
|
|
77
|
-
|
|
77
|
+
arr = a if is_list(a) else [a]
|
|
78
|
+
return np.repeat(np.arange(len(arr)), arr)
|
|
78
79
|
|
|
79
80
|
|
|
80
81
|
def eval_monad_first(a):
|
|
@@ -163,7 +164,7 @@ def eval_monad_grade_up(a):
|
|
|
163
164
|
>[[1] [2] [3]] --> [2 1 0]
|
|
164
165
|
|
|
165
166
|
"""
|
|
166
|
-
return kg_argsort(
|
|
167
|
+
return kg_argsort(kg_asarray(a))
|
|
167
168
|
|
|
168
169
|
|
|
169
170
|
def eval_monad_grade_down(a):
|
|
@@ -174,7 +175,7 @@ def eval_monad_grade_down(a):
|
|
|
174
175
|
See [Grade-Up].
|
|
175
176
|
|
|
176
177
|
"""
|
|
177
|
-
return kg_argsort(
|
|
178
|
+
return kg_argsort(kg_asarray(a), descending=True)
|
|
178
179
|
|
|
179
180
|
|
|
180
181
|
def eval_monad_groupby(a):
|
|
@@ -194,12 +195,12 @@ def eval_monad_groupby(a):
|
|
|
194
195
|
="hello foo" --> [[0] [1] [2 3] [4 7 8] [5] [6]]
|
|
195
196
|
|
|
196
197
|
"""
|
|
197
|
-
|
|
198
|
-
if
|
|
199
|
-
return
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
return
|
|
198
|
+
arr = kg_asarray(a)
|
|
199
|
+
if arr.size == 0:
|
|
200
|
+
return arr
|
|
201
|
+
vals, inverse = np.unique(arr, return_inverse=True)
|
|
202
|
+
groups = [np.where(inverse == i)[0] for i in range(len(vals))]
|
|
203
|
+
return kg_asarray(groups)
|
|
203
204
|
|
|
204
205
|
|
|
205
206
|
def eval_monad_list(a):
|
|
@@ -217,7 +218,7 @@ def eval_monad_list(a):
|
|
|
217
218
|
if isinstance(a, KGChar):
|
|
218
219
|
return str(a)
|
|
219
220
|
if isinstance(a, KGSym):
|
|
220
|
-
np.asarray([a],dtype=object) # np
|
|
221
|
+
return np.asarray([a],dtype=object) # np interprets ':foo" as ':fo"
|
|
221
222
|
return np.asarray([a])
|
|
222
223
|
|
|
223
224
|
|
klongpy/repl.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import threading
|
|
3
|
+
import time
|
|
4
|
+
import os
|
|
5
|
+
import importlib.resources
|
|
6
|
+
|
|
7
|
+
from . import KlongInterpreter
|
|
8
|
+
from .utils import CallbackEvent
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def start_loop(loop: asyncio.AbstractEventLoop, stop_event: asyncio.Event) -> None:
|
|
12
|
+
asyncio.set_event_loop(loop)
|
|
13
|
+
loop.run_until_complete(stop_event.wait())
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def setup_async_loop(debug: bool = False, slow_callback_duration: float = 86400.0):
|
|
17
|
+
loop = asyncio.new_event_loop()
|
|
18
|
+
loop.slow_callback_duration = slow_callback_duration
|
|
19
|
+
if debug:
|
|
20
|
+
loop.set_debug(True)
|
|
21
|
+
stop_event = asyncio.Event()
|
|
22
|
+
thread = threading.Thread(target=start_loop, args=(loop, stop_event), daemon=True)
|
|
23
|
+
thread.start()
|
|
24
|
+
return loop, thread, stop_event
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def cleanup_async_loop(loop: asyncio.AbstractEventLoop, loop_thread: threading.Thread, stop_event: asyncio.Event, debug: bool = False, name: str | None = None) -> None:
|
|
28
|
+
if loop.is_closed():
|
|
29
|
+
return
|
|
30
|
+
|
|
31
|
+
loop.call_soon_threadsafe(stop_event.set)
|
|
32
|
+
loop_thread.join()
|
|
33
|
+
|
|
34
|
+
pending_tasks = asyncio.all_tasks(loop=loop)
|
|
35
|
+
if len(pending_tasks) > 0:
|
|
36
|
+
if name:
|
|
37
|
+
print(f"WARNING: pending tasks in {name} loop")
|
|
38
|
+
for task in pending_tasks:
|
|
39
|
+
loop.call_soon_threadsafe(task.cancel)
|
|
40
|
+
while len(asyncio.all_tasks(loop=loop)) > 0:
|
|
41
|
+
time.sleep(0)
|
|
42
|
+
|
|
43
|
+
loop.stop()
|
|
44
|
+
|
|
45
|
+
if not loop.is_closed():
|
|
46
|
+
loop.close()
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def append_pkg_resource_path_KLONGPATH() -> None:
|
|
50
|
+
with importlib.resources.as_file(importlib.resources.files('klongpy')) as pkg_path:
|
|
51
|
+
pkg_lib_path = os.path.join(pkg_path, 'lib')
|
|
52
|
+
klongpath = os.environ.get('KLONGPATH', '.:lib')
|
|
53
|
+
klongpath = f"{klongpath}:{pkg_lib_path}" if klongpath else str(pkg_lib_path)
|
|
54
|
+
os.environ['KLONGPATH'] = klongpath
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def create_repl(debug: bool = False):
|
|
58
|
+
io_loop, io_thread, io_stop = setup_async_loop(debug=debug)
|
|
59
|
+
klong_loop, klong_thread, klong_stop = setup_async_loop(debug=debug)
|
|
60
|
+
|
|
61
|
+
append_pkg_resource_path_KLONGPATH()
|
|
62
|
+
|
|
63
|
+
klong = KlongInterpreter()
|
|
64
|
+
shutdown_event = CallbackEvent()
|
|
65
|
+
klong['.system'] = {'ioloop': io_loop, 'klongloop': klong_loop, 'closeEvent': shutdown_event}
|
|
66
|
+
|
|
67
|
+
return klong, (io_loop, io_thread, io_stop, klong_loop, klong_thread, klong_stop)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def cleanup_repl(loops, debug: bool = False) -> None:
|
|
71
|
+
io_loop, io_thread, io_stop, klong_loop, klong_thread, klong_stop = loops
|
|
72
|
+
cleanup_async_loop(io_loop, io_thread, io_stop, debug=debug, name='io_loop')
|
|
73
|
+
cleanup_async_loop(klong_loop, klong_thread, klong_stop, debug=debug, name='klong_loop')
|
klongpy/sys_fn.py
CHANGED
|
@@ -13,7 +13,7 @@ import numpy
|
|
|
13
13
|
|
|
14
14
|
from .core import (KGChannel, KGChannelDir, KGLambda, KGSym, KlongException,
|
|
15
15
|
is_dict, is_empty, is_list, kg_asarray, kg_read, kg_write, np,
|
|
16
|
-
reserved_fn_args, reserved_fn_symbol_map, safe_eq)
|
|
16
|
+
reserved_fn_args, reserved_fn_symbol_map, safe_eq, safe_inspect)
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
def eval_sys_append_channel(x):
|
|
@@ -341,7 +341,7 @@ def _handle_import(item):
|
|
|
341
341
|
if n_args <= len(reserved_fn_args):
|
|
342
342
|
item = KGLambda(item, args=reserved_fn_args[:n_args])
|
|
343
343
|
else:
|
|
344
|
-
args =
|
|
344
|
+
args = safe_inspect(item, follow_wrapped=True)
|
|
345
345
|
if 'args' in args:
|
|
346
346
|
item = KGLambda(item, args=None, wildcard=True)
|
|
347
347
|
n_args = 3
|
|
@@ -405,7 +405,7 @@ def _import_module(klong, x, from_set=None):
|
|
|
405
405
|
module = import_module_from_sys(x)
|
|
406
406
|
|
|
407
407
|
export_items = module.__dict__.get("klongpy_exports") or module.__dict__
|
|
408
|
-
ffn = lambda p: p[0] in from_set if from_set is not None else lambda p: not p[0].startswith("__")
|
|
408
|
+
ffn = (lambda p: p[0] in from_set) if from_set is not None else (lambda p: not p[0].startswith("__"))
|
|
409
409
|
|
|
410
410
|
ctx = klong._context.pop()
|
|
411
411
|
try:
|
klongpy/sys_fn_ipc.py
CHANGED
|
@@ -919,7 +919,6 @@ def eval_sys_fn_create_ipc_server(klong, x):
|
|
|
919
919
|
if "x" is 0, then the server is closed and existing client connections are dropped.
|
|
920
920
|
|
|
921
921
|
"""
|
|
922
|
-
global _ipc_tcp_server
|
|
923
922
|
x = str(x)
|
|
924
923
|
parts = x.split(":")
|
|
925
924
|
bind = parts[0] if len(parts) > 1 else None
|
klongpy/web/sys_fn_web.py
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
import sys
|
|
3
|
+
import asyncio
|
|
4
|
+
import concurrent.futures
|
|
3
5
|
|
|
4
6
|
from aiohttp import web
|
|
5
7
|
|
|
@@ -113,7 +115,17 @@ def eval_sys_fn_create_web_server(klong, x, y, z):
|
|
|
113
115
|
site = web.TCPSite(runner, bind, port)
|
|
114
116
|
await site.start()
|
|
115
117
|
|
|
116
|
-
|
|
118
|
+
# create the server task in the ioloop thread and capture the task handle
|
|
119
|
+
server_loop = klong['.system']['ioloop']
|
|
120
|
+
task_future = concurrent.futures.Future()
|
|
121
|
+
|
|
122
|
+
def _start():
|
|
123
|
+
task = asyncio.create_task(start_server())
|
|
124
|
+
task_future.set_result(task)
|
|
125
|
+
|
|
126
|
+
server_loop.call_soon_threadsafe(_start)
|
|
127
|
+
server_task = task_future.result()
|
|
128
|
+
|
|
117
129
|
return WebServerHandle(bind, port, runner, server_task)
|
|
118
130
|
|
|
119
131
|
|
|
@@ -129,7 +141,7 @@ def eval_sys_fn_shutdown_web_server(klong, x):
|
|
|
129
141
|
x = x.a.fn
|
|
130
142
|
if isinstance(x, WebServerHandle) and x.runner is not None:
|
|
131
143
|
print("shutting down web server")
|
|
132
|
-
klong['.system']['ioloop'].
|
|
144
|
+
asyncio.run_coroutine_threadsafe(x.shutdown(), klong['.system']['ioloop']).result()
|
|
133
145
|
return 1
|
|
134
146
|
return 0
|
|
135
147
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: klongpy
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.9
|
|
4
4
|
Summary: High-Performance Klong array language with rich Python integration.
|
|
5
5
|
Author: Brian Guarraci
|
|
6
6
|
License: MIT
|
|
@@ -9,38 +9,48 @@ Classifier: License :: OSI Approved :: MIT License
|
|
|
9
9
|
Requires-Python: <3.13,>=3.9
|
|
10
10
|
Description-Content-Type: text/markdown
|
|
11
11
|
License-File: LICENSE
|
|
12
|
-
Requires-Dist: numpy
|
|
13
|
-
Provides-Extra: cuda102
|
|
14
|
-
Requires-Dist: cupy-cuda102 ; extra == 'cuda102'
|
|
15
|
-
Provides-Extra: cuda110
|
|
16
|
-
Requires-Dist: cupy-cuda110 ; extra == 'cuda110'
|
|
17
|
-
Provides-Extra: cuda111
|
|
18
|
-
Requires-Dist: cupy-cuda111 ; extra == 'cuda111'
|
|
19
|
-
Provides-Extra: cuda11x
|
|
20
|
-
Requires-Dist: cupy-cuda11x ; extra == 'cuda11x'
|
|
21
|
-
Provides-Extra: cuda12x
|
|
22
|
-
Requires-Dist: cupy-cuda12x ; extra == 'cuda12x'
|
|
12
|
+
Requires-Dist: numpy~=1.26.4
|
|
23
13
|
Provides-Extra: cupy
|
|
24
|
-
Requires-Dist: cupy
|
|
25
|
-
Provides-Extra:
|
|
26
|
-
Requires-Dist:
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
Requires-Dist:
|
|
31
|
-
|
|
32
|
-
Requires-Dist:
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
Requires-Dist: colorama ==0.4.6 ; extra == 'repl'
|
|
36
|
-
Provides-Extra: rocm-4-3
|
|
37
|
-
Requires-Dist: cupy-rocm-4-3 ; extra == 'rocm-4-3'
|
|
14
|
+
Requires-Dist: cupy; extra == "cupy"
|
|
15
|
+
Provides-Extra: cuda12x
|
|
16
|
+
Requires-Dist: cupy-cuda12x; extra == "cuda12x"
|
|
17
|
+
Provides-Extra: cuda11x
|
|
18
|
+
Requires-Dist: cupy-cuda11x; extra == "cuda11x"
|
|
19
|
+
Provides-Extra: cuda111
|
|
20
|
+
Requires-Dist: cupy-cuda111; extra == "cuda111"
|
|
21
|
+
Provides-Extra: cuda110
|
|
22
|
+
Requires-Dist: cupy-cuda110; extra == "cuda110"
|
|
23
|
+
Provides-Extra: cuda102
|
|
24
|
+
Requires-Dist: cupy-cuda102; extra == "cuda102"
|
|
38
25
|
Provides-Extra: rocm-5-0
|
|
39
|
-
Requires-Dist: cupy-rocm-5-0
|
|
26
|
+
Requires-Dist: cupy-rocm-5-0; extra == "rocm-5-0"
|
|
27
|
+
Provides-Extra: rocm-4-3
|
|
28
|
+
Requires-Dist: cupy-rocm-4-3; extra == "rocm-4-3"
|
|
29
|
+
Provides-Extra: repl
|
|
30
|
+
Requires-Dist: colorama==0.4.6; extra == "repl"
|
|
40
31
|
Provides-Extra: web
|
|
41
|
-
Requires-Dist: aiohttp
|
|
32
|
+
Requires-Dist: aiohttp==3.9.4; extra == "web"
|
|
33
|
+
Provides-Extra: db
|
|
34
|
+
Requires-Dist: pandas==2.2.2; extra == "db"
|
|
35
|
+
Requires-Dist: duckdb==1.3.0; extra == "db"
|
|
42
36
|
Provides-Extra: ws
|
|
43
|
-
Requires-Dist: websockets
|
|
37
|
+
Requires-Dist: websockets==12.0; extra == "ws"
|
|
38
|
+
Provides-Extra: full
|
|
39
|
+
Requires-Dist: colorama==0.4.6; extra == "full"
|
|
40
|
+
Requires-Dist: aiohttp==3.9.4; extra == "full"
|
|
41
|
+
Requires-Dist: pandas==2.2.2; extra == "full"
|
|
42
|
+
Requires-Dist: duckdb==1.3.0; extra == "full"
|
|
43
|
+
Requires-Dist: websockets==12.0; extra == "full"
|
|
44
|
+
Dynamic: author
|
|
45
|
+
Dynamic: classifier
|
|
46
|
+
Dynamic: description
|
|
47
|
+
Dynamic: description-content-type
|
|
48
|
+
Dynamic: license
|
|
49
|
+
Dynamic: license-file
|
|
50
|
+
Dynamic: provides-extra
|
|
51
|
+
Dynamic: requires-dist
|
|
52
|
+
Dynamic: requires-python
|
|
53
|
+
Dynamic: summary
|
|
44
54
|
|
|
45
55
|
|
|
46
56
|

|
|
@@ -54,8 +64,6 @@ Requires-Dist: websockets ==12.0 ; extra == 'ws'
|
|
|
54
64
|
[](https://pepy.tech/project/klongpy)
|
|
55
65
|
[](https://opensource.org/licenses/MIT)
|
|
56
66
|
|
|
57
|
-
[](https://twitter.com/klongpy)
|
|
58
|
-
|
|
59
67
|
# KlongPy: High-Performance Array Programming in Python
|
|
60
68
|
|
|
61
69
|
KlongPy is a Python adaptation of the [Klong](https://t3x.org/klong) [array language](https://en.wikipedia.org/wiki/Array_programming), known for its high-performance vectorized operations that leverage the power of NumPy. Embracing a "batteries included" philosophy, KlongPy combines built-in modules with Python's expansive ecosystem, facilitating rapid application development with Klong's succinct syntax.
|
|
@@ -334,6 +342,31 @@ $ curl http://localhost:8888
|
|
|
334
342
|
['Hello, Klong World! ' 0 1 2 3 4 5 6 7 8 9]
|
|
335
343
|
```
|
|
336
344
|
|
|
345
|
+
You can also spin up this server directly inside the REPL:
|
|
346
|
+
|
|
347
|
+
```kgpy
|
|
348
|
+
?> .py("klongpy.web")
|
|
349
|
+
?> data::!10
|
|
350
|
+
?> index::{x; "Hello, Klong World! ", data}
|
|
351
|
+
?> get:::{}; get,"/",index
|
|
352
|
+
?> post:::{}
|
|
353
|
+
?> h::.web(8888;get;post)
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
And from another terminal:
|
|
357
|
+
|
|
358
|
+
```bash
|
|
359
|
+
$ curl http://localhost:8888
|
|
360
|
+
['Hello, Klong World! ' 0 1 2 3 4 5 6 7 8 9]
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
Stop the server with:
|
|
364
|
+
|
|
365
|
+
```kgpy
|
|
366
|
+
?> .webc(h)
|
|
367
|
+
1
|
|
368
|
+
```
|
|
369
|
+
|
|
337
370
|
## Conclusion
|
|
338
371
|
|
|
339
372
|
These examples are designed to illustrate the "batteries included" approach, ease of use and diverse applications of KlongPy, making it a versatile choice for various programming needs.
|
|
@@ -390,6 +423,7 @@ KlongPy is effectively a superset of the Klong language, but has some key differ
|
|
|
390
423
|
* Infinite precision: The main difference in this implementation of Klong is the lack of infinite precision. By using NumPy we are restricted to doubles.
|
|
391
424
|
* Python integration: Most notably, the ".py" command allows direct import of Python modules into the current Klong context.
|
|
392
425
|
* KlongPy aims to be more "batteries included" approach to modules and contains additional features such as IPC, Web service, Websockets, etc.
|
|
426
|
+
* For array operations, KlongPy matches the shape of array arguments differently. Compare the results of an expression like `[1 2]+[[3 4][5 6]]` which in Klong produces `[[4 5] [7 8]]` but in KlongPy produces `[[4 6] [6 8]]`.
|
|
393
427
|
|
|
394
428
|
# Related
|
|
395
429
|
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
klongpy/__init__.py,sha256=8K0RIIZh2QLdOBA68oym_MeEwO33ffcDOZAU3etJ7kA,107
|
|
2
2
|
klongpy/adverbs.py,sha256=W6HAL4rxWV6djrILnahRqw8o6-Yq9pUWIUznSJG8-f0,12566
|
|
3
3
|
klongpy/backend.py,sha256=qA2DulWLVtlOYs3opwJIod0XZVTLCMvqjuttK2_-CHo,2897
|
|
4
|
-
klongpy/core.py,sha256=
|
|
5
|
-
klongpy/dyads.py,sha256=
|
|
6
|
-
klongpy/interpreter.py,sha256=
|
|
7
|
-
klongpy/monads.py,sha256=
|
|
8
|
-
klongpy/
|
|
9
|
-
klongpy/
|
|
4
|
+
klongpy/core.py,sha256=bie-UnhYufNmB7hSKhKmR_dT4X4K8EdrUTiBzk9A3ck,27460
|
|
5
|
+
klongpy/dyads.py,sha256=KkVfKaFnxU6_1-wJNIuwagN_NVwySt9XVP6Sqg0dack,31636
|
|
6
|
+
klongpy/interpreter.py,sha256=AH4LEv3jhgmupgJrvwqMbW_F2EwpmZFKtOMdKc_SJzM,23981
|
|
7
|
+
klongpy/monads.py,sha256=HTpBQKNVbZ7KBnS1xxOsNAwKWUG8xAOXLQIOQR4022g,14076
|
|
8
|
+
klongpy/repl.py,sha256=zwWrxU_KbNfbXAdAJS_wN56bi12w3Jm7HGH6m6Tv0yY,2542
|
|
9
|
+
klongpy/sys_fn.py,sha256=jVhBcUBmKWAgCnZpueyRebYxaWn7fvyDoI_fNx3nw3Y,23729
|
|
10
|
+
klongpy/sys_fn_ipc.py,sha256=okKzZm-FKCyWlDqpL7bnUDZjLqkQVSQK9h20ERItma0,35660
|
|
10
11
|
klongpy/sys_fn_timer.py,sha256=wY9Xx_zA3LOHnAm9ltFvr9OAOXNcnoWQVNYIniDQlSQ,2945
|
|
11
12
|
klongpy/sys_var.py,sha256=x4Jq5JwnQrwdU54vxh03JuunKBb6nQoAGOoxvQAk1w4,4322
|
|
12
13
|
klongpy/utils.py,sha256=nuevztUECi73_H0rrUX8Og19GUEdgkULp5tb2pirhVU,749
|
|
@@ -19,7 +20,7 @@ klongpy/db/sys_fn_kvs.py,sha256=--FTMQyKdn8vBx0qG4mOBzdd2rtBfm0rW4c0AnB0G94,4114
|
|
|
19
20
|
klongpy/lib/csv.kg,sha256=w6FvenQmkl0EF2kieQxxloNOFgZbt3M9IMSYSdImJcA,1498
|
|
20
21
|
klongpy/lib/edt.kg,sha256=aJsjCxAkbfzwxWg8mF9nRDbCCt2fqtCWv0CPsq4SIHw,707
|
|
21
22
|
klongpy/lib/eigenv.kg,sha256=_9iypo9qozqx5HfQR2h8aOx8kYlHGjuZ6NJ4vrfesd4,2118
|
|
22
|
-
klongpy/lib/help.kg,sha256=
|
|
23
|
+
klongpy/lib/help.kg,sha256=p2OobNtxpXMz5xR0DCJwnBonR8Ot86q62r41F2cy4qQ,4309
|
|
23
24
|
klongpy/lib/huffman.kg,sha256=u4vmvzhZ6_-rLgjUH_6HAFMuvqDxhrH1ZWqyNywStOk,637
|
|
24
25
|
klongpy/lib/math.kg,sha256=pIFXlEV_-NzyYobqk3ofvW4Mte-4EqYmSWkwkA8AyLw,10118
|
|
25
26
|
klongpy/lib/nstat.kg,sha256=1besBcCWp067aOv86jHPVmPQMtEWpC41uU1W_pET4yI,18920
|
|
@@ -29,10 +30,11 @@ klongpy/lib/spline.kg,sha256=8kVvX9gsieR5f2hZDx0M_oKmJl7CP6Nn0hJssdcXkwk,1454
|
|
|
29
30
|
klongpy/lib/time.kg,sha256=tJPknPGXHa_239PBMscg2VSaJ3ZV6SVRbADtTtE1a_4,359
|
|
30
31
|
klongpy/lib/util.kg,sha256=LXwPtmcYZUv3Vs5eFt-1RqTvr7cn-_RKLKaSnLLN5gc,440
|
|
31
32
|
klongpy/web/__init__.py,sha256=lM_40n_qzwYSx_OFi4RqFwsxQe8GGBtJm3_GLLxn9-8,101
|
|
32
|
-
klongpy/web/sys_fn_web.py,sha256=
|
|
33
|
+
klongpy/web/sys_fn_web.py,sha256=usfcEWAQyTK0cppPZPefzr_raYoMzZUeh8WZDuvaAMU,5064
|
|
33
34
|
klongpy/ws/__init__.py,sha256=GKl5804W3XNkDER0mkdFf-4cIVjgeP9M3tfNg_tCv3c,114
|
|
34
35
|
klongpy/ws/sys_fn_ws.py,sha256=Ba6MmmTS6cWHlruGgNznNXXPQX2zaBQmAEwkz9ge7sk,18300
|
|
35
|
-
klongpy-0.6.
|
|
36
|
+
klongpy-0.6.9.data/scripts/kgpy,sha256=nDF66LclUBK6qILB2dgJ9VKAlGlPKMQfoJ4CvH5d_UQ,12119
|
|
37
|
+
klongpy-0.6.9.dist-info/licenses/LICENSE,sha256=Nk1iW-tI7n0z8R2jMqVm9SuFIoa8iukXsFcJoPqQgns,1071
|
|
36
38
|
tests/__init__.py,sha256=xdETQVm8SNlASCru27aks1KZwSs2Oh-0NR5zrccSqb0,221
|
|
37
39
|
tests/gen_join_over.py,sha256=ZiliJS6Lz0rqaZAfAeJFbPOU109PRF0QFE6262a4TPU,3684
|
|
38
40
|
tests/gen_py_suite.py,sha256=ihXihalZkjN9Poy3m-1FVGdZy_YYWS0CEkY68pDgGso,2495
|
|
@@ -50,23 +52,26 @@ tests/perf_sys_fn_db.py,sha256=mjoskDzjRJQL4xZULtOm4_y0r2GT8WhgGR1RdhNLjqw,5015
|
|
|
50
52
|
tests/perf_vector.py,sha256=p76TVCS5szG0BX7rzquvKb7xikxUQOUmQttvTS0EH44,976
|
|
51
53
|
tests/test_accel.py,sha256=apKi0PN9l9ztDLTwtdo6esWCl7_Yo97xljfNY0q9hug,6305
|
|
52
54
|
tests/test_df_cache.py,sha256=vK36B0ENNwTYgv_JvRZimum9EEoPw-qgi4BWQWHRqQQ,3302
|
|
55
|
+
tests/test_eval_monad_list.py,sha256=pP7hDxpWIJSg6MD8tya5ctS9d5Z6I2xHnrFbDutkMQY,846
|
|
53
56
|
tests/test_examples.py,sha256=hfpCQlcLR4pjUeSk9uAhtgnsjLEHLc_sfvRRWq8W1iY,2131
|
|
54
57
|
tests/test_extra_suite.py,sha256=WPIA_UcP5BVvZIlkiDMUx_877ts3rYoVLo0grQh4JW8,12967
|
|
55
58
|
tests/test_file_cache.py,sha256=lkE4bXPR9qp6wRdNHcG0f0Fb_BBB-2oocRttmCbXT7A,7733
|
|
56
|
-
tests/test_interop.py,sha256=
|
|
59
|
+
tests/test_interop.py,sha256=1ja39E4p_aHpc1hdWbldAid2MIWCv2raNamYxMrSfwk,5805
|
|
60
|
+
tests/test_kg_asarray.py,sha256=7k2BjUWz7ojRKou2wpEniLNtV9JLTQPkDLmXKh3gph4,2854
|
|
57
61
|
tests/test_kgtests.py,sha256=7lUgKYt9TQiYxAXWqE4VuaZo7frhbzSd9ofa6_pWBLE,2190
|
|
58
62
|
tests/test_known_bugs.py,sha256=shBMW2rwQV0CLtrPZZ54TDFII7W5fxi7AOpZyvbydM8,6481
|
|
59
63
|
tests/test_prog.py,sha256=8P019M-hiW9jxe9FgQS3Q7wqxoTrkhclMTk6xO4HBWU,3311
|
|
60
|
-
tests/
|
|
64
|
+
tests/test_reshape_strings.py,sha256=sZlzKnBuv9ZvUm3sU_PXLr2SXA8gL92n8F0h4gjV528,1063
|
|
65
|
+
tests/test_suite.py,sha256=iBLRwnzEPZWh86lJ9rXUrUMqRUCh-kDl11TsrfPSrWM,73100
|
|
61
66
|
tests/test_suite_file.py,sha256=zZLXK1xEQgUH0OO9J53jkF4j0rjbgIXsWlsNo5puriA,4027
|
|
62
67
|
tests/test_sys_fn.py,sha256=iGoalDXEw2LEIRGYgYIhc1rc-rU5wCNbRyvcr6nYoPw,15695
|
|
63
68
|
tests/test_sys_fn_db.py,sha256=iX5sSJGzVMaN_GnhNQE_b6mWCIUYrDmE25KdubXNQL8,2471
|
|
64
69
|
tests/test_sys_fn_ipc.py,sha256=tV7uCGxXQCHZQ2CkffD4j5h01TYBe1ozC_wImiANyHA,21605
|
|
65
70
|
tests/test_sys_fn_timer.py,sha256=VUfi1AK9SCzA9g5IgbfpdEMW2AP4-9dgVOpJXhMMQhA,4447
|
|
71
|
+
tests/test_sys_fn_web.py,sha256=TQX2R_4DCR2YOnhITFpTibL6_4UNbAphiix0fOuFv0Q,1552
|
|
66
72
|
tests/test_util.py,sha256=k4UCR0avjWNxo_jO710x32AAMOAePOx79EOt58wL584,7942
|
|
67
73
|
tests/utils.py,sha256=QrLAR4-WXVSDrC60qhl78_NE-3idp0mgLJmSZ5seyWw,3721
|
|
68
|
-
klongpy-0.6.
|
|
69
|
-
klongpy-0.6.
|
|
70
|
-
klongpy-0.6.
|
|
71
|
-
klongpy-0.6.
|
|
72
|
-
klongpy-0.6.7.dist-info/RECORD,,
|
|
74
|
+
klongpy-0.6.9.dist-info/METADATA,sha256=49HaLNk5_XaBlB3Uq6U_hFN8w04_rYHBd7-ipbU-Cbc,17249
|
|
75
|
+
klongpy-0.6.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
76
|
+
klongpy-0.6.9.dist-info/top_level.txt,sha256=1dsI3MhtibeAZuB7WPW58dpNgyGvXLX5EEHqm46NQYo,14
|
|
77
|
+
klongpy-0.6.9.dist-info/RECORD,,
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
from klongpy import KlongInterpreter
|
|
5
|
+
from klongpy.core import KGSym
|
|
6
|
+
from tests.utils import kg_equal
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TestEvalMonadList(unittest.TestCase):
|
|
10
|
+
|
|
11
|
+
def setUp(self):
|
|
12
|
+
self.klong = KlongInterpreter()
|
|
13
|
+
|
|
14
|
+
def test_int(self):
|
|
15
|
+
r = self.klong(',1')
|
|
16
|
+
self.assertTrue(kg_equal(r, np.asarray([1])))
|
|
17
|
+
|
|
18
|
+
def test_symbol(self):
|
|
19
|
+
r = self.klong(',:foo')
|
|
20
|
+
self.assertTrue(r.dtype == object)
|
|
21
|
+
self.assertEqual(len(r), 1)
|
|
22
|
+
self.assertEqual(r[0], KGSym('foo'))
|
|
23
|
+
|
|
24
|
+
def test_string(self):
|
|
25
|
+
r = self.klong(',"xyz"')
|
|
26
|
+
self.assertTrue(kg_equal(r, np.asarray(['xyz'], dtype=object)))
|
|
27
|
+
|
|
28
|
+
def test_list(self):
|
|
29
|
+
r = self.klong(',[1]')
|
|
30
|
+
self.assertTrue(kg_equal(r, np.asarray([[1]], dtype=object)))
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
if __name__ == '__main__':
|
|
34
|
+
unittest.main()
|
tests/test_interop.py
CHANGED
tests/test_kg_asarray.py
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
from klongpy.core import kg_asarray, KGChar, KGSym
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class TestKGAsArray(unittest.TestCase):
|
|
9
|
+
|
|
10
|
+
def test_kg_asarray_scalar_int(self):
|
|
11
|
+
x = 42
|
|
12
|
+
arr = kg_asarray(x)
|
|
13
|
+
assert np.isarray(arr)
|
|
14
|
+
assert arr.dtype == int
|
|
15
|
+
assert arr.shape == ()
|
|
16
|
+
assert arr.item() == 42
|
|
17
|
+
|
|
18
|
+
def test_kg_asarray_scalar_float(self):
|
|
19
|
+
x = 3.14
|
|
20
|
+
arr = kg_asarray(x)
|
|
21
|
+
assert np.isarray(arr)
|
|
22
|
+
assert arr.dtype == float
|
|
23
|
+
assert arr.item() == 3.14
|
|
24
|
+
|
|
25
|
+
def test_kg_asarray_empty_list(self):
|
|
26
|
+
x = []
|
|
27
|
+
arr = kg_asarray(x)
|
|
28
|
+
assert np.isarray(arr)
|
|
29
|
+
assert arr.dtype == float # default dtype for empty list
|
|
30
|
+
assert arr.size == 0
|
|
31
|
+
|
|
32
|
+
def test_kg_asarray_string(self):
|
|
33
|
+
s = "hello"
|
|
34
|
+
arr = kg_asarray(s)
|
|
35
|
+
assert np.isarray(arr)
|
|
36
|
+
assert arr.dtype == object # assuming KGChar is object dtype
|
|
37
|
+
assert "".join(arr) == "hello"
|
|
38
|
+
|
|
39
|
+
def test_kg_asarray_list_of_ints(self):
|
|
40
|
+
x = [1, 2, 3]
|
|
41
|
+
arr = kg_asarray(x)
|
|
42
|
+
assert np.isarray(arr)
|
|
43
|
+
assert arr.dtype == int
|
|
44
|
+
assert np.array_equal(arr, [1, 2, 3])
|
|
45
|
+
|
|
46
|
+
def test_kg_asarray_nested_list_uniform(self):
|
|
47
|
+
x = [[1, 2], [3, 4]]
|
|
48
|
+
arr = kg_asarray(x)
|
|
49
|
+
assert np.isarray(arr)
|
|
50
|
+
assert arr.shape == (2,2)
|
|
51
|
+
assert arr.dtype == int
|
|
52
|
+
assert np.array_equal(arr, [[1,2],[3,4]])
|
|
53
|
+
|
|
54
|
+
@unittest.skip("what is the expected behavior for this case?")
|
|
55
|
+
def test_kg_asarray_nested_list_heterogeneous(self):
|
|
56
|
+
# should embedded strings be expanded to individual characters?
|
|
57
|
+
x = [[1, 2], "abc", [3.14, None]]
|
|
58
|
+
arr = kg_asarray(x)
|
|
59
|
+
assert np.isarray(arr)
|
|
60
|
+
# Because of heterogeneous data, dtype should be object.
|
|
61
|
+
assert arr.dtype == object
|
|
62
|
+
# Check that sub-elements are arrays
|
|
63
|
+
assert np.isarray(arr[0])
|
|
64
|
+
assert np.isarray(arr[1])
|
|
65
|
+
assert np.isarray(arr[2])
|
|
66
|
+
|
|
67
|
+
def test_kg_asarray_already_array(self):
|
|
68
|
+
x = np.array([1, 2, 3])
|
|
69
|
+
arr = kg_asarray(x)
|
|
70
|
+
# Should return as-is because already suitable dtype
|
|
71
|
+
assert arr is x
|
|
72
|
+
|
|
73
|
+
def test_kg_asarray_jagged_list(self):
|
|
74
|
+
x = [[1, 2, 3], [4, 5], [6]]
|
|
75
|
+
arr = kg_asarray(x)
|
|
76
|
+
assert np.isarray(arr)
|
|
77
|
+
# Jagged => object dtype
|
|
78
|
+
assert arr.dtype == object
|
|
79
|
+
# Each element should be an array
|
|
80
|
+
assert all(np.isarray(e) for e in arr)
|
|
81
|
+
assert np.array_equal(arr[0], [1, 2, 3])
|
|
82
|
+
assert np.array_equal(arr[1], [4, 5])
|
|
83
|
+
assert np.array_equal(arr[2], [6])
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def benchmark_kg_asarray(self):
|
|
87
|
+
import timeit
|
|
88
|
+
x = [[1, 2], [3, 4]]
|
|
89
|
+
print(timeit.timeit(lambda: kg_asarray(x), number=100_000))
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
if __name__ == "__main__":
|
|
93
|
+
# run the benchmark
|
|
94
|
+
TestKGAsArray().benchmark_kg_asarray()
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
from klongpy import KlongInterpreter
|
|
5
|
+
from tests.utils import kg_equal
|
|
6
|
+
|
|
7
|
+
class TestReshapeStrings(unittest.TestCase):
|
|
8
|
+
def setUp(self):
|
|
9
|
+
self.klong = KlongInterpreter()
|
|
10
|
+
|
|
11
|
+
def test_reshape_string_len1(self):
|
|
12
|
+
r = self.klong('[2 2]:^"a"')
|
|
13
|
+
self.assertTrue(kg_equal(r, np.asarray(["aa", "aa"], dtype=object)))
|
|
14
|
+
|
|
15
|
+
def test_reshape_string_len2(self):
|
|
16
|
+
r = self.klong('[2 2]:^"ab"')
|
|
17
|
+
self.assertTrue(kg_equal(r, np.asarray(["ab", "ab"], dtype=object)))
|
|
18
|
+
|
|
19
|
+
def test_reshape_string_len3(self):
|
|
20
|
+
r = self.klong('[2 2]:^"abc"')
|
|
21
|
+
self.assertTrue(kg_equal(r, np.asarray(["ab", "ca"], dtype=object)))
|
|
22
|
+
|
|
23
|
+
def test_reshape_string_len4(self):
|
|
24
|
+
r = self.klong('[2 2]:^"abcd"')
|
|
25
|
+
self.assertTrue(kg_equal(r, np.asarray(["ab", "cd"], dtype=object)))
|
|
26
|
+
|
|
27
|
+
def test_reshape_string_larger_shape(self):
|
|
28
|
+
r = self.klong('[3 3]:^"abcd"')
|
|
29
|
+
self.assertTrue(kg_equal(r, np.asarray(["abc", "dab", "cda"], dtype=object)))
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
if __name__ == '__main__':
|
|
33
|
+
unittest.main()
|
tests/test_suite.py
CHANGED
|
@@ -484,6 +484,7 @@ class TestCoreSuite(unittest.TestCase):
|
|
|
484
484
|
self.assert_eval_cmp('5$1.23', '"1.23 "')
|
|
485
485
|
self.assert_eval_cmp('5$-1.23', '"-1.23"')
|
|
486
486
|
self.assert_eval_cmp('6$-1.23', '"-1.23 "')
|
|
487
|
+
self.assert_eval_cmp('[1]$[1]', '["1"]')
|
|
487
488
|
self.assert_eval_cmp('(-10)$0', '" 0"')
|
|
488
489
|
self.assert_eval_cmp('(-10)$123', '" 123"')
|
|
489
490
|
self.assert_eval_cmp('(-10)$-123', '" -123"')
|
tests/test_sys_fn_web.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import socket
|
|
3
|
+
import unittest
|
|
4
|
+
|
|
5
|
+
import aiohttp
|
|
6
|
+
|
|
7
|
+
from klongpy.repl import create_repl, cleanup_repl
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TestSysFnWeb(unittest.TestCase):
|
|
11
|
+
def setUp(self):
|
|
12
|
+
self.klong, self.loops = create_repl()
|
|
13
|
+
(self.ioloop, self.ioloop_thread, self.io_stop,
|
|
14
|
+
self.klongloop, self.klongloop_thread, self.klong_stop) = self.loops
|
|
15
|
+
self.handle = None
|
|
16
|
+
|
|
17
|
+
def tearDown(self):
|
|
18
|
+
if self.handle is not None and self.handle.task is not None:
|
|
19
|
+
asyncio.run_coroutine_threadsafe(self.handle.shutdown(), self.ioloop).result()
|
|
20
|
+
cleanup_repl(self.loops)
|
|
21
|
+
|
|
22
|
+
def _free_port(self):
|
|
23
|
+
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
24
|
+
s.bind(("", 0))
|
|
25
|
+
port = s.getsockname()[1]
|
|
26
|
+
s.close()
|
|
27
|
+
return port
|
|
28
|
+
|
|
29
|
+
def test_web_server_start_and_stop(self):
|
|
30
|
+
klong = self.klong
|
|
31
|
+
port = self._free_port()
|
|
32
|
+
|
|
33
|
+
klong('.py("klongpy.web")')
|
|
34
|
+
klong('index::{x;"hello"}')
|
|
35
|
+
klong('get:::{}')
|
|
36
|
+
klong('get,"/",index')
|
|
37
|
+
klong('post:::{}')
|
|
38
|
+
handle = klong(f'h::.web({port};get;post)')
|
|
39
|
+
self.handle = handle
|
|
40
|
+
|
|
41
|
+
async def fetch():
|
|
42
|
+
async with aiohttp.ClientSession() as session:
|
|
43
|
+
async with session.get(f"http://localhost:{port}/") as resp:
|
|
44
|
+
return await resp.text()
|
|
45
|
+
|
|
46
|
+
response = asyncio.run_coroutine_threadsafe(fetch(), self.ioloop).result()
|
|
47
|
+
self.assertEqual(response, "hello")
|
|
48
|
+
|
|
49
|
+
asyncio.run_coroutine_threadsafe(handle.shutdown(), self.ioloop).result()
|
|
50
|
+
|
|
File without changes
|
|
File without changes
|
|
File without changes
|