klongpy 0.6.9__py3-none-any.whl → 0.7.0__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.
Files changed (64) hide show
  1. klongpy/__init__.py +19 -1
  2. klongpy/adverbs.py +5 -5
  3. klongpy/autograd.py +308 -0
  4. klongpy/backend.py +167 -99
  5. klongpy/backends/__init__.py +94 -0
  6. klongpy/backends/base.py +320 -0
  7. klongpy/backends/numpy_backend.py +122 -0
  8. klongpy/backends/torch_backend.py +995 -0
  9. klongpy-0.6.9.data/scripts/kgpy → klongpy/cli.py +65 -88
  10. klongpy/core.py +228 -108
  11. klongpy/db/sys_fn_db.py +4 -3
  12. klongpy/dyads.py +159 -28
  13. klongpy/interpreter.py +31 -3
  14. klongpy/monads.py +39 -3
  15. klongpy/repl.py +21 -3
  16. klongpy/sys_fn.py +128 -17
  17. klongpy/sys_fn_autograd.py +290 -0
  18. klongpy/sys_fn_ipc.py +18 -6
  19. klongpy/sys_fn_timer.py +13 -3
  20. klongpy/web/sys_fn_web.py +14 -4
  21. klongpy-0.7.0.dist-info/METADATA +493 -0
  22. klongpy-0.7.0.dist-info/RECORD +48 -0
  23. {klongpy-0.6.9.dist-info → klongpy-0.7.0.dist-info}/WHEEL +1 -1
  24. klongpy-0.7.0.dist-info/entry_points.txt +2 -0
  25. {klongpy-0.6.9.dist-info → klongpy-0.7.0.dist-info}/top_level.txt +0 -1
  26. klongpy-0.6.9.dist-info/METADATA +0 -448
  27. klongpy-0.6.9.dist-info/RECORD +0 -77
  28. tests/__init__.py +0 -6
  29. tests/gen_join_over.py +0 -119
  30. tests/gen_py_suite.py +0 -77
  31. tests/gen_test_fn.py +0 -259
  32. tests/perf_async.py +0 -25
  33. tests/perf_avg.py +0 -18
  34. tests/perf_duckdb.py +0 -32
  35. tests/perf_gen.py +0 -38
  36. tests/perf_ipc_overhead.py +0 -34
  37. tests/perf_join.py +0 -53
  38. tests/perf_load.py +0 -17
  39. tests/perf_prog.py +0 -18
  40. tests/perf_serdes.py +0 -52
  41. tests/perf_sys_fn_db.py +0 -263
  42. tests/perf_vector.py +0 -40
  43. tests/test_accel.py +0 -227
  44. tests/test_df_cache.py +0 -85
  45. tests/test_eval_monad_list.py +0 -34
  46. tests/test_examples.py +0 -64
  47. tests/test_extra_suite.py +0 -382
  48. tests/test_file_cache.py +0 -185
  49. tests/test_interop.py +0 -180
  50. tests/test_kg_asarray.py +0 -94
  51. tests/test_kgtests.py +0 -65
  52. tests/test_known_bugs.py +0 -206
  53. tests/test_prog.py +0 -107
  54. tests/test_reshape_strings.py +0 -33
  55. tests/test_suite.py +0 -1480
  56. tests/test_suite_file.py +0 -153
  57. tests/test_sys_fn.py +0 -420
  58. tests/test_sys_fn_db.py +0 -88
  59. tests/test_sys_fn_ipc.py +0 -587
  60. tests/test_sys_fn_timer.py +0 -133
  61. tests/test_sys_fn_web.py +0 -50
  62. tests/test_util.py +0 -233
  63. tests/utils.py +0 -126
  64. {klongpy-0.6.9.dist-info → klongpy-0.7.0.dist-info}/licenses/LICENSE +0 -0
tests/test_extra_suite.py DELETED
@@ -1,382 +0,0 @@
1
- import unittest
2
-
3
- from utils import *
4
-
5
- from klongpy import KlongInterpreter
6
- from klongpy.core import (KGChar, KGSym, is_float, is_integer, rec_flatten)
7
-
8
-
9
- # add tests not included in the original kg suite
10
- class TestExtraCoreSuite(unittest.TestCase):
11
-
12
- def assert_eval_cmp(self, a, b, klong=None):
13
- self.assertTrue(eval_cmp(a, b, klong=klong))
14
-
15
- def test_negate_array_result_type(self):
16
- """ ensure the result type of negating an array is correct """
17
- klong = KlongInterpreter()
18
- r = klong("-1]")
19
- self.assertTrue(is_integer(r))
20
- r = klong("-1.0]")
21
- self.assertTrue(is_float(r))
22
- r = klong("-[1 2 3]")
23
- self.assertTrue(r.dtype == int)
24
- r = klong("-[1.0 2.0 3.0]")
25
- self.assertTrue(r.dtype == float)
26
-
27
- def test_match_empty_array_to_undefined_symbol(self):
28
- """ symbol is undefined so does not match the empty array """
29
- klong = KlongInterpreter()
30
- r = klong('[]~.a')
31
- self.assertEqual(r, 0)
32
-
33
- def test_vectorized(self):
34
- klong = KlongInterpreter()
35
- r = klong("2*!1000")
36
- self.assertTrue(kg_equal(r, np.arange(1000)*2))
37
-
38
- # This is different behavior than Klong, which doesn't allow at/index on dictionaries.
39
- def test_dict_at_index(self):
40
- klong = KlongInterpreter()
41
- klong("D:::{[1 2]}")
42
- r = klong("D@1")
43
- self.assertEqual(r, 2)
44
- with self.assertRaises(KeyError):
45
- klong("D@2")
46
-
47
- def test_each_dict_with_mixed_types(self):
48
- klong = KlongInterpreter()
49
- klong["D"] = {object: [1, 2, 3]}
50
- klong(".p'D")
51
-
52
- def test_apply_range(self):
53
- klong = KlongInterpreter()
54
- r = klong("{x}@,!100")
55
- self.assertTrue(kg_equal(r, np.arange(100)))
56
- klong("avg::{(+/x)%#x}")
57
- r = klong("avg@,!100")
58
- self.assertEqual(r,49.5)
59
-
60
- def test_eval_quote_string(self):
61
- klong = KlongInterpreter()
62
- r = klong(':"hello"')
63
- self.assertTrue(r is None)
64
-
65
- def test_array_identity(self):
66
- klong = KlongInterpreter()
67
- r = klong('[]')
68
- self.assertTrue(kg_equal(r, np.array([],dtype=object)))
69
- r = klong('[1]')
70
- self.assertTrue(kg_equal(r, np.array([1],dtype=object)))
71
- r = klong('[[1]]')
72
- self.assertTrue(kg_equal(r, np.array([[1]],dtype=object)))
73
- r = klong('[[1] [2]]')
74
- self.assertTrue(kg_equal(r, np.array([[1],[2]],dtype=object)))
75
- r = klong('[[1] [2 3]]')
76
- self.assertTrue(kg_equal(r, np.array([[1],[2,3]],dtype=object)))
77
- r = klong('[[[1]] [2 3]]')
78
- self.assertTrue(kg_equal(r, np.array([[[1]],[2,3]],dtype=object)))
79
- r = klong('[[1] [[2 3]]]')
80
- self.assertTrue(kg_equal(r, np.array([[1],[[2,3]]],dtype=object)))
81
- r = klong('[[[1]] [[2 3]]]')
82
- self.assertTrue(kg_equal(r, np.array([[[1]],[[2,3]]],dtype=object)))
83
-
84
- def test_jagged_array_identity(self):
85
- klong = KlongInterpreter()
86
- r = klong('[[0] [[1]]]')
87
- q = np.array([[0],[[1]]],dtype=object)
88
- self.assertTrue(kg_equal(r, q))
89
-
90
- def test_match_array(self):
91
- klong = KlongInterpreter()
92
- r = klong('(^[1])~0')
93
- self.assertFalse(r is False)
94
- self.assertEqual(r,0)
95
-
96
- def test_prime(self):
97
- klong = KlongInterpreter()
98
- klong('prime::{&/x!:\\2+!_x^1%2}') # note \\ ==> \
99
- r = klong("prime(251)")
100
- self.assertTrue(is_integer(r))
101
- self.assertEqual(r, 1)
102
-
103
- def test_floor_as_int(self):
104
- klong = KlongInterpreter()
105
- r = klong('_30%2')
106
- self.assertTrue(is_integer(r))
107
- self.assertEqual(r, 15)
108
- r = klong('_[30 20]%2')
109
- for x in r:
110
- self.assertTrue(is_integer(x))
111
- self.assertTrue(kg_equal(r, [15, 10]))
112
-
113
- def test_drop_string(self):
114
- klong = KlongInterpreter()
115
- klong("""
116
- NAMES:::{}
117
- AN::{[k n g];.p(x);k::(x?",")@0;n::.rs(k#x);g::.rs((k+1)_x);NAMES,n,,g}")
118
- """)
119
- r = klong('S::\"""John"",""boy""\"')
120
- self.assertEqual(r,'"John","boy"')
121
- r = klong('AN(S);NAMES')
122
- self.assertEqual(r['John'], "boy")
123
-
124
- # read 123456 from "123456 hello" requires parsing by space
125
- def test_read_number_from_various_strings(self):
126
- klong = KlongInterpreter()
127
- # DIFF: Klong will puke on this with undefined
128
- r = klong('.rs("123456 hello")')
129
- self.assertEqual(r, 123456)
130
-
131
- def test_join_nested_arrays(self):
132
- self.assert_eval_cmp('[[0 0] [1 1]],,2,2', '[[0 0] [1 1] [2 2]]')
133
-
134
- def test_range_over_nested_arrays(self):
135
- self.assert_eval_cmp('?[[0 0] [1 1] 3 3]', '[[0 0] [1 1] 3]')
136
- self.assert_eval_cmp('?[[0 0] [1 1] [1 1]]', '[[0 0] [1 1]]')
137
- self.assert_eval_cmp('?[[0 0] [1 1] [1 1] 3 3]', '[[0 0] [1 1] 3]')
138
- self.assert_eval_cmp('?[[[0 0] [0 0] [1 1]] [1 1] [1 1]]', '[[[0 0] [0 0] [1 1]] [1 1]]')
139
- self.assert_eval_cmp('?[[[0 0] [0 0] [1 1]] [1 1] [1 1] 3 3]', '[[[0 0] [0 0] [1 1]] [1 1] 3]')
140
- self.assert_eval_cmp('?[[0 0] [1 0] [2 0] [3 0] [4 1] [4 2] [4 3] [3 4] [2 4] [3 3] [4 3] [3 2] [2 2] [1 2]]', '[[0 0] [1 0] [2 0] [3 0] [4 1] [4 2] [4 3] [3 4] [2 4] [3 3] [3 2] [2 2] [1 2]]')
141
-
142
- def test_sum_over_nested_arrays(self):
143
- """
144
- sum over nested arrays should reduce
145
- """
146
- self.assert_eval_cmp('+/[1 2 3]', '6')
147
- self.assert_eval_cmp('+/[[1 2 3]]', '[1 2 3]')
148
- self.assert_eval_cmp('+/[[1 2 3] [4 5 6]]', '[5 7 9]')
149
- self.assert_eval_cmp('+/[[1 2 3] [4 5 6] [7 8 9]]', '[12 15 18]')
150
-
151
- def test_power_preserve_type(self):
152
- klong = KlongInterpreter()
153
- r = klong("10^5")
154
- self.assertTrue(is_integer(r))
155
- r = klong("10.5^5")
156
- self.assertTrue(is_float(r))
157
-
158
- def test_join_array_dict(self):
159
- klong = KlongInterpreter()
160
- klong("""
161
- N::{d:::{[:s 0] [:c []]};d,:p,x;d,:n,y}
162
- D::N(1%0;"/")
163
- n::N(D;"x")
164
- """)
165
- klong('(D?:c),n')
166
-
167
- def test_join_sym_string(self):
168
- klong = KlongInterpreter()
169
- r = klong(':p,"hello"')
170
- self.assertTrue(isinstance(r[0],KGSym))
171
- self.assertEqual(r[1],"hello")
172
-
173
- def test_join_sym_dict(self):
174
- klong = KlongInterpreter()
175
- klong("D:::{[1 2]}")
176
- r = klong(":p,D")
177
- self.assertTrue(isinstance(r[0],KGSym))
178
- self.assertTrue(isinstance(r[1],dict))
179
-
180
- def test_complex_join_dict_create(self):
181
- klong = KlongInterpreter()
182
- klong("""
183
- N::{d:::{[:s 0] [:c []]};d,:p,x;d,:n,y}
184
- D::N(1%0;"/")
185
- """)
186
- r = klong('q::N(D;"hello")')
187
- self.assertEqual(klong("q?:s"), 0)
188
- self.assertTrue(kg_equal(klong("q?:c"), []))
189
- self.assertEqual(klong("q?:n"), "hello")
190
- self.assertTrue(isinstance(klong("q?:p"), dict))
191
-
192
- def test_join_sym_int(self):
193
- klong = KlongInterpreter()
194
- r = klong(":p,43")
195
- self.assertTrue(isinstance(r[0],KGSym))
196
- self.assertTrue(isinstance(r[1],int))
197
-
198
- def test_integer_divide_clamp_to_int(self):
199
- klong = KlongInterpreter()
200
- r = klong("24:%2")
201
- self.assertTrue(is_integer(r))
202
- r = klong("[12 24]:%2")
203
- self.assertTrue(is_integer(r[0]))
204
- self.assertTrue(is_integer(r[1]))
205
-
206
- def test_enumerate_float(self):
207
- klong = KlongInterpreter()
208
- with self.assertRaises(RuntimeError):
209
- klong("!(24%2)")
210
- r = klong("!(24:%2)")
211
- self.assertEqual(len(r), 12)
212
-
213
- def test_at_index_single_index(self):
214
- klong = KlongInterpreter()
215
- def _char_test(x):
216
- if not isinstance(x,KGChar):
217
- raise RuntimeError("should be char")
218
- klong['ischar'] = _char_test
219
- klong('ischar("hello"@2)')
220
-
221
- def test_at_index_array_index(self):
222
- klong = KlongInterpreter()
223
- def _str_test(x):
224
- if isinstance(x,KGChar):
225
- raise RuntimeError("should be string")
226
- klong['isstr'] = _str_test
227
- klong('isstr("hello"@[2])')
228
- klong('isstr("hello"@[1 2 2])')
229
-
230
- def test_module_fallback(self):
231
- klong = KlongInterpreter()
232
- r = klong("""
233
- pi::3.14
234
- .module(:test)
235
- foo::pi
236
- .module(0)
237
- foo
238
- """)
239
- self.assertTrue(r,3.14)
240
-
241
- def test_read_enotation(self):
242
- klong = KlongInterpreter()
243
- r = klong('t::1.0e8;')
244
- self.assertEqual(r, 1.0e8)
245
- r = klong('t::1.0e-8;')
246
- self.assertEqual(r, 1.0e-8)
247
-
248
- def test_read_list(self):
249
- klong = KlongInterpreter()
250
- r = klong('[]')
251
- self.assertTrue(kg_equal(r,[]))
252
- klong = KlongInterpreter()
253
- r = klong('[1 2 3 4]')
254
- self.assertTrue(kg_equal(r,[1,2,3,4]))
255
- klong = KlongInterpreter()
256
- r = klong('[1 2 3 4 ]') # add spaces as found in suite
257
- self.assertTrue(kg_equal(r,[1,2,3,4]))
258
-
259
- def test_read_list_as_arg(self):
260
- """
261
- make list span lines to test whitespace skip
262
- """
263
- t = """
264
- zop::{x}
265
- zop([
266
- ["hello"]
267
- ])
268
- """
269
- klong = KlongInterpreter()
270
- klong('zap::{x}')
271
- r = klong('zap([])')
272
- self.assertTrue(kg_equal(r,[]))
273
- r = klong(t)
274
- self.assertTrue(kg_equal(r,[["hello"]]))
275
-
276
- def test_sys_comment(self):
277
- t = """.comment("end-of-comment")
278
- abcdefghijklmnopqrstuvwxyz
279
- ABCDEFGHIJKLMNOPQRSTUVWXYZ
280
- 0123456789
281
- ~!@#$%^&*()_+-=`[]{}:;"'<,>.
282
- end-of-comment
283
-
284
- A::1;
285
- A
286
- """
287
- klong = KlongInterpreter()
288
- r = klong.exec(t)
289
- # verify that we are returning both A::1 and A operation results
290
- self.assertTrue(kg_equal(r,[1,1]))
291
- r = klong(t)
292
- self.assertEqual(r, 1)
293
-
294
- def test_eval_array(self):
295
- klong = KlongInterpreter()
296
- r = klong('[[[1] [2] [3]] [1 2 3]]')
297
- self.assertTrue(kg_equal(r,[[[1],[2],[3]],[1,2,3]]))
298
-
299
- def test_dot_f(self):
300
- klong = KlongInterpreter()
301
- klong('fr::{:[x;"hello";.f(1)]}')
302
- self.assert_eval_cmp('fr(0)', '"hello"', klong=klong)
303
- self.assert_eval_cmp('fr(1)', '"hello"', klong=klong)
304
- klong('fr::{:[x<1;x;.f(x%2)]}')
305
- self.assert_eval_cmp('fr(0)', '0', klong=klong)
306
- self.assert_eval_cmp('fr(1)', '0.5', klong=klong)
307
- self.assert_eval_cmp('fr(12)', '0.75', klong=klong)
308
- klong("fr::{:[0=x;[];1,.f(x-1)]}")
309
- self.assert_eval_cmp('fr(0)', '[]', klong=klong)
310
- self.assert_eval_cmp('fr(1)', '[1]', klong=klong)
311
- self.assert_eval_cmp('fr(2)', '[1 1]', klong=klong)
312
- self.assert_eval_cmp('fr(3)', '[1 1 1]', klong=klong)
313
-
314
- def test_harness(self):
315
- klong = KlongInterpreter()
316
- klong('err::0;')
317
- klong('wl::{.w(x);.p("")}')
318
- klong('fail::{err::1;.d("failed: ");.p(x);.d("expected: ");wl(z);.d("got: ");wl(y);[]}')
319
- klong('t::{:[~y~z;fail(x;y;z);[]]}')
320
- r = klong("err")
321
- self.assertEqual(r, 0)
322
- klong("A::1")
323
- klong('t("A::1" ; A ; 1)')
324
- r = klong("err")
325
- self.assertEqual(r, 0)
326
- r = klong('t("A::1" ; A ; 2)')
327
- r = klong("err")
328
- self.assertEqual(r, 1)
329
-
330
- def test_vector_math(self):
331
- klong = KlongInterpreter()
332
- r = klong("!10000000")
333
- x = np.arange(10000000)
334
- self.assertTrue(np.equal(x, r).all())
335
-
336
- r = klong("1+!10000000")
337
- x = np.add(x, 1)
338
- self.assertTrue(np.equal(x, r).all())
339
-
340
- r = klong("2*1+!10000000")
341
- x = np.multiply(x, 2)
342
- self.assertTrue(np.equal(x, r).all())
343
-
344
- r = klong("3%2*1+!10000000")
345
- x = np.divide(3, x)
346
- self.assertTrue(np.equal(x, r).all())
347
-
348
- r = klong("10-3%2*1+!10000000")
349
- x = np.subtract(10, x)
350
- self.assertTrue(np.equal(x, r).all())
351
-
352
- r = klong("(1+!10)&3")
353
- x = np.minimum(np.add(np.arange(10), 1), 3)
354
- self.assertTrue(np.equal(x, r).all())
355
-
356
- r = klong("(1+!10)!5")
357
- x = np.fmod(np.add(np.arange(10), 1), 5)
358
- self.assertTrue(np.equal(x, r).all())
359
-
360
- def test_converge(self):
361
- klong = KlongInterpreter()
362
- r = klong('{(x+2%x)%2}:~2')
363
- self.assertTrue(np.isclose(r,1.41421356237309504))
364
-
365
- def test_scan_converge(self):
366
- klong = KlongInterpreter()
367
- r = klong('{(x+2%x)%2}\~2')
368
- e = np.asarray([2. , 1.5 , 1.41666667, 1.41421569])
369
- self.assertTrue(np.isclose(r,e).all())
370
-
371
- r = klong(',/\~["a" ["b"] "c"]')
372
- e = np.asarray([["a",["b"],"c"],["a","b","c"],"abc"],dtype=object)
373
- x = r
374
- self.assertTrue(rec_flatten(rec_fn2(e,x, lambda a,b: a == b)).all())
375
-
376
- def test_converge_as_fn(self):
377
- klong = KlongInterpreter()
378
- klong('s::{(x+2%x)%2}:~2')
379
- r = klong('s')
380
- self.assertTrue(np.isclose(r,1.41421356237309504))
381
- r = klong('s()')
382
- self.assertTrue(np.isclose(r,1.41421356237309504))
tests/test_file_cache.py DELETED
@@ -1,185 +0,0 @@
1
- import os
2
- import platform
3
- import random
4
- import tempfile
5
- import threading
6
- import time
7
- import unittest
8
- from multiprocessing.pool import ThreadPool
9
-
10
- from klongpy.db.file_cache import FileCache
11
-
12
- # TODO: add MacOS RAM disk
13
- # hdiutil attach -nomount ram://$((2 * 1024 * 100))
14
- # diskutil eraseVolume HFS+ RAMDisk /dev/disk3
15
- # https://stackoverflow.com/questions/1854/how-to-identify-which-os-python-is-running-on
16
-
17
- def gen_data():
18
- return bytearray(os.urandom(random.randint(10,50)))
19
-
20
- def gen_file(tmp_dir=None):
21
- tmp_dir = tmp_dir or ("/dev/shm" if platform.system() == "Linux" else None)
22
- f = tempfile.NamedTemporaryFile(delete=False, dir=tmp_dir)
23
- d = gen_data()
24
- f.write(d)
25
- f.seek(0)
26
- return f,d,len(d)
27
-
28
- class FileCacheTests(unittest.TestCase):
29
- def setUp(self):
30
- self.file_contents = {i:gen_file() for i in range(10)}
31
- # make cache just under capactiy to hold all 4 files to force eviction
32
- self.file_cache = FileCache(max_memory=(sum([x[2] for x in self.file_contents.values()]) - 1))
33
-
34
- def test_get_file(self):
35
- info = self.file_contents[0]
36
- contents = self.file_cache.get_file(info[0].name)
37
- self.assertEqual(self.file_cache.current_memory_usage, info[2])
38
- self.assertTrue(info[0].name in self.file_cache.file_futures)
39
- self.assertEqual(self.file_cache.file_futures[info[0].name][1], info[2])
40
- self.assertEqual(contents, info[1])
41
- self.assertEqual(len(self.file_cache.file_access_times), 1)
42
- self.assertEqual(info[0].name, self.file_cache.file_access_times[0][1])
43
-
44
- def test_get_file_with_eviction(self):
45
- for i in range(2):
46
- for info in self.file_contents.values():
47
- expected_memory_usage = self.file_cache.current_memory_usage + info[2]
48
- if expected_memory_usage > self.file_cache.max_memory:
49
- oldest_size = self.file_cache.file_futures[self.file_cache.file_access_times[0][1]][1]
50
- expected_memory_usage -= oldest_size
51
- contents = self.file_cache.get_file(info[0].name)
52
- self.assertEqual(self.file_cache.current_memory_usage, expected_memory_usage)
53
- self.assertTrue(info[0].name in self.file_cache.file_futures)
54
- self.assertEqual(self.file_cache.file_futures[info[0].name][1], info[2])
55
- self.assertEqual(contents, info[1])
56
- self.assertEqual(info[0].name, self.file_cache.file_access_times[-1][1])
57
-
58
- def test_get_file_multithreaded(self):
59
- def get_file_thread(file, data, size):
60
- self.assertEqual(self.file_cache.get_file(file.name), data)
61
-
62
- with ThreadPool() as pool:
63
- pool.starmap(get_file_thread, random.choices(list(self.file_contents.values()), k=100))
64
-
65
- def test_unload_file(self):
66
- for info in self.file_contents.values():
67
- self.file_cache.get_file(info[0].name)
68
- self.assertEqual(self.file_cache.current_memory_usage, info[2])
69
- self.file_cache.unload_file(info[0].name)
70
- self.assertFalse(info[0].name in self.file_cache.file_futures)
71
- self.assertEqual(self.file_cache.current_memory_usage, 0)
72
- self.assertEqual(len(self.file_cache.file_access_times), 0)
73
-
74
- def test_unload_file_multithreaded(self):
75
- def unload_file_thread(file, data, size):
76
- self.file_cache.get_file(file.name)
77
- self.assertTrue(file.name in self.file_cache.file_futures)
78
- self.file_cache.unload_file(file.name)
79
- self.assertFalse(file.name in self.file_cache.file_futures)
80
-
81
- with ThreadPool() as pool:
82
- for _ in range(10):
83
- pool.starmap(unload_file_thread, list(self.file_contents.values()))
84
-
85
- def test_update_file(self):
86
- info = self.file_contents[0]
87
- content = self.file_cache.get_file(info[0].name)
88
- self.assertEqual(content, info[1])
89
-
90
- # Update the file in the cache
91
- new_contents = gen_data()
92
- updated = self.file_cache.update_file(info[0].name, new_contents)
93
- self.assertTrue(updated)
94
- self.assertTrue(info[0].name in self.file_cache.file_futures)
95
- self.assertEqual(self.file_cache.current_memory_usage, len(new_contents))
96
- self.assertEqual(len(self.file_cache.file_access_times), 1)
97
- self.assertEqual(info[0].name, self.file_cache.file_access_times[0][1])
98
-
99
- # Check that the updated file is returned
100
- content = self.file_cache.get_file(info[0].name)
101
- self.assertEqual(content, new_contents)
102
-
103
- self.file_cache.unload_file(info[0].name)
104
- self.assertFalse(info[0].name in self.file_cache.file_futures)
105
- self.assertEqual(self.file_cache.current_memory_usage, 0)
106
- self.assertEqual(len(self.file_cache.file_access_times), 0)
107
-
108
- def test_update_file_multithreaded(self):
109
- def update_file_thread(file, data, size):
110
- info = (file, data, size)
111
- updated = self.file_cache.update_file(info[0].name, info[1])
112
- self.assertTrue(updated)
113
- content = self.file_cache.get_file(info[0].name)
114
- self.assertEqual(content, info[1])
115
-
116
- # Update the file in the cache
117
- new_contents = gen_data()
118
- updated = self.file_cache.update_file(info[0].name, new_contents)
119
- self.assertTrue(updated)
120
-
121
- # Check that the updated file is returned
122
- content = self.file_cache.get_file(info[0].name)
123
- self.assertEqual(content, new_contents)
124
-
125
- self.file_cache.unload_file(info[0].name)
126
- self.assertFalse(info[0].name in self.file_cache.file_futures)
127
-
128
- with ThreadPool() as pool:
129
- for _ in range(10):
130
- pool.starmap(update_file_thread, list(self.file_contents.values()))
131
-
132
- def test_update_file_multithreaded_expected(self):
133
- info = self.file_contents[0]
134
- name = info[0].name
135
- self.failed = False
136
- self.run = True
137
-
138
- # Create a list of expected values for each thread
139
- expected_values = [gen_data() for _ in range(10)]
140
- all_read_expected_values = [info[1], *expected_values]
141
-
142
- def update_file_thread(file_name, contents):
143
- while self.run:
144
- updated = self.file_cache.update_file(file_name, contents)
145
- while not updated:
146
- updated = self.file_cache.update_file(file_name, contents)
147
-
148
- def get_file_thread():
149
- while self.run:
150
- updated_file_1_content = self.file_cache.get_file(name)
151
- if updated_file_1_content not in all_read_expected_values:
152
- self.failed = True
153
- self.assertIn(updated_file_1_content, all_read_expected_values)
154
-
155
- # Update the test file in multiple threads
156
- threads = []
157
- for i in range(10):
158
- thread = threading.Thread(target=update_file_thread, args=(name, expected_values[i]))
159
- thread.start()
160
- threads.append(thread)
161
-
162
- for i in range(100):
163
- thread = threading.Thread(target=get_file_thread)
164
- thread.start()
165
- threads.append(thread)
166
-
167
- time.sleep(3)
168
- self.run = False
169
-
170
- # Wait for all threads to complete
171
- for thread in threads:
172
- thread.join()
173
-
174
- self.assertFalse(self.failed)
175
-
176
- # Check that the final contents of the file match one of the expected values
177
- updated_file_1_content = self.file_cache.get_file(name)
178
- self.assertIn(updated_file_1_content, expected_values)
179
-
180
- def tearDown(self):
181
- for v in self.file_contents.values():
182
- os.unlink(v[0].name)
183
-
184
- if __name__ == '__main__':
185
- unittest.main()