klongpy 0.6.8__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 CHANGED
@@ -259,17 +259,19 @@ def kg_asarray(a):
259
259
  arr : ndarray
260
260
  The converted input data as a NumPy array, where all elements and sub-arrays are also NumPy arrays.
261
261
  """
262
+ if isinstance(a, str):
263
+ return str_to_chr_arr(a)
262
264
  try:
263
265
  arr = np.asarray(a)
264
266
  if arr.dtype.kind not in ['O','i','f']:
265
267
  raise ValueError
266
268
  except (np.VisibleDeprecationWarning, ValueError):
267
269
  try:
268
- arr = np.asarray(a,dtype=object)
270
+ arr = np.asarray(a, dtype=object)
269
271
  except ValueError:
270
272
  arr = [x.tolist() if np.isarray(x) else x for x in a]
271
- arr = np.asarray(arr,dtype=object)
272
- 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)
273
275
  return arr
274
276
 
275
277
 
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
- return "".join(r) if j else r
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
- return "".join(r) if j else r
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/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
- [" a$b" "Format2"
34
- "a=real; Format 'b', pad with 'a' blanks or to align to x.y digits"]
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
- return np.concatenate([np.zeros(x, dtype=int) + i for i,x in enumerate(a if is_list(a) else [a])])
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(str_to_chr_arr(a) if isinstance(a,str) else a)
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(str_to_chr_arr(a) if isinstance(a,str) else a, descending=True)
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
- q = np.asarray(str_to_chr_arr(a) if isinstance(a, str) else a)
198
- if len(q) == 0:
199
- return q
200
- a = q.argsort()
201
- r = np.split(a, np.where(q[a][1:] != q[a][:-1])[0] + 1)
202
- return np.asarray(r, dtype=object)
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 interpets ':foo" as ':fo"
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
@@ -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
- server_task = klong['.system']['ioloop'].create_task(start_server())
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'].run_until_complete(x.shutdown())
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
1
+ Metadata-Version: 2.4
2
2
  Name: klongpy
3
- Version: 0.6.8
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
@@ -10,37 +10,47 @@ Requires-Python: <3.13,>=3.9
10
10
  Description-Content-Type: text/markdown
11
11
  License-File: LICENSE
12
12
  Requires-Dist: numpy~=1.26.4
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"
23
13
  Provides-Extra: cupy
24
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"
25
+ Provides-Extra: 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"
31
+ Provides-Extra: web
32
+ Requires-Dist: aiohttp==3.9.4; extra == "web"
25
33
  Provides-Extra: db
26
34
  Requires-Dist: pandas==2.2.2; extra == "db"
27
- Requires-Dist: duckdb==1.0.0; extra == "db"
35
+ Requires-Dist: duckdb==1.3.0; extra == "db"
36
+ Provides-Extra: ws
37
+ Requires-Dist: websockets==12.0; extra == "ws"
28
38
  Provides-Extra: full
29
39
  Requires-Dist: colorama==0.4.6; extra == "full"
30
40
  Requires-Dist: aiohttp==3.9.4; extra == "full"
31
41
  Requires-Dist: pandas==2.2.2; extra == "full"
32
- Requires-Dist: duckdb==1.0.0; extra == "full"
42
+ Requires-Dist: duckdb==1.3.0; extra == "full"
33
43
  Requires-Dist: websockets==12.0; extra == "full"
34
- Provides-Extra: repl
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"
38
- Provides-Extra: rocm-5-0
39
- Requires-Dist: cupy-rocm-5-0; extra == "rocm-5-0"
40
- Provides-Extra: web
41
- Requires-Dist: aiohttp==3.9.4; extra == "web"
42
- Provides-Extra: ws
43
- Requires-Dist: websockets==12.0; extra == "ws"
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
  ![Unit Tests](https://github.com/briangu/klongpy/workflows/Unit%20Tests/badge.svg)
@@ -332,6 +342,31 @@ $ curl http://localhost:8888
332
342
  ['Hello, Klong World! ' 0 1 2 3 4 5 6 7 8 9]
333
343
  ```
334
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
+
335
370
  ## Conclusion
336
371
 
337
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.
@@ -388,6 +423,7 @@ KlongPy is effectively a superset of the Klong language, but has some key differ
388
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.
389
424
  * Python integration: Most notably, the ".py" command allows direct import of Python modules into the current Klong context.
390
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]]`.
391
427
 
392
428
  # Related
393
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=CRXiq9epjjUGGFEJW46KhrakuU-hvQ6zlMyVLmAubmA,27396
5
- klongpy/dyads.py,sha256=Fhr2u2PgLKKyBOaiB8ofyDl3-TDceoSmgfrLBj5WyFI,31247
4
+ klongpy/core.py,sha256=bie-UnhYufNmB7hSKhKmR_dT4X4K8EdrUTiBzk9A3ck,27460
5
+ klongpy/dyads.py,sha256=KkVfKaFnxU6_1-wJNIuwagN_NVwySt9XVP6Sqg0dack,31636
6
6
  klongpy/interpreter.py,sha256=AH4LEv3jhgmupgJrvwqMbW_F2EwpmZFKtOMdKc_SJzM,23981
7
- klongpy/monads.py,sha256=6PoLROije2u-ojQZPu2f5FYnRav02Bx8eovrm2C29NY,14158
8
- klongpy/sys_fn.py,sha256=MnnP0tHHnbs2krfiYN88jWbZLsZ2jqPQmZlOrbMVuxk,23725
9
- klongpy/sys_fn_ipc.py,sha256=O_QAEvJtkmcom-CdPOqpqhED7O8euqIn6Gc2NGT45ZA,35687
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=xlnDZOZNS3vYKdgcr0oBSvUSYBlcuq6nDhFCnQ9KbsU,4260
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=En9_8Xh1uIaGz8CRyWsYuwpt0rbpNWvDCKFJ6KZpU5o,4710
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.8.data/scripts/kgpy,sha256=nDF66LclUBK6qILB2dgJ9VKAlGlPKMQfoJ4CvH5d_UQ,12119
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=efUVa-IB43TecUhOm1rfTlK9G6kcf0U4q6EEVf06gaE,5837
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/test_suite.py,sha256=cKTH9ICQUFPyM2xcmQNuS_jueDHLD2ukVMeM_KstTbQ,73051
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.8.dist-info/LICENSE,sha256=Nk1iW-tI7n0z8R2jMqVm9SuFIoa8iukXsFcJoPqQgns,1071
69
- klongpy-0.6.8.dist-info/METADATA,sha256=3znv7SQ4tsMpZLeR5g6OhfQ2kj8eK_CKHM21I7XYmSQ,16421
70
- klongpy-0.6.8.dist-info/WHEEL,sha256=tZoeGjtWxWRfdplE7E3d45VPlLNQnvbKiYnx7gwAy8A,92
71
- klongpy-0.6.8.dist-info/top_level.txt,sha256=1dsI3MhtibeAZuB7WPW58dpNgyGvXLX5EEHqm46NQYo,14
72
- klongpy-0.6.8.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,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.45.1)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -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
@@ -5,7 +5,6 @@ from datetime import datetime
5
5
  from utils import *
6
6
 
7
7
  from klongpy import KlongInterpreter
8
- from klongpy.core import KGChar
9
8
 
10
9
 
11
10
  class TestPythonInterop(unittest.TestCase):
@@ -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"')
@@ -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