synapse 2.152.0__py311-none-any.whl → 2.154.0__py311-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.

Potentially problematic release.


This version of synapse might be problematic. Click here for more details.

Files changed (87) hide show
  1. synapse/axon.py +19 -16
  2. synapse/cortex.py +203 -15
  3. synapse/exc.py +0 -2
  4. synapse/lib/ast.py +42 -23
  5. synapse/lib/autodoc.py +2 -2
  6. synapse/lib/cache.py +16 -1
  7. synapse/lib/cell.py +5 -5
  8. synapse/lib/httpapi.py +198 -2
  9. synapse/lib/layer.py +5 -2
  10. synapse/lib/modelrev.py +36 -3
  11. synapse/lib/node.py +2 -5
  12. synapse/lib/parser.py +1 -1
  13. synapse/lib/schemas.py +51 -0
  14. synapse/lib/snap.py +10 -0
  15. synapse/lib/storm.lark +24 -4
  16. synapse/lib/storm.py +98 -19
  17. synapse/lib/storm_format.py +1 -1
  18. synapse/lib/stormhttp.py +11 -4
  19. synapse/lib/stormlib/auth.py +16 -2
  20. synapse/lib/stormlib/backup.py +1 -0
  21. synapse/lib/stormlib/basex.py +2 -0
  22. synapse/lib/stormlib/cell.py +7 -0
  23. synapse/lib/stormlib/compression.py +3 -0
  24. synapse/lib/stormlib/cortex.py +1168 -0
  25. synapse/lib/stormlib/ethereum.py +1 -0
  26. synapse/lib/stormlib/graph.py +2 -0
  27. synapse/lib/stormlib/hashes.py +5 -0
  28. synapse/lib/stormlib/hex.py +6 -0
  29. synapse/lib/stormlib/infosec.py +6 -1
  30. synapse/lib/stormlib/ipv6.py +1 -0
  31. synapse/lib/stormlib/iters.py +58 -1
  32. synapse/lib/stormlib/json.py +5 -0
  33. synapse/lib/stormlib/mime.py +1 -0
  34. synapse/lib/stormlib/model.py +19 -3
  35. synapse/lib/stormlib/modelext.py +1 -0
  36. synapse/lib/stormlib/notifications.py +2 -0
  37. synapse/lib/stormlib/pack.py +2 -0
  38. synapse/lib/stormlib/random.py +1 -0
  39. synapse/lib/stormlib/smtp.py +0 -7
  40. synapse/lib/stormlib/stats.py +223 -0
  41. synapse/lib/stormlib/stix.py +8 -0
  42. synapse/lib/stormlib/storm.py +1 -0
  43. synapse/lib/stormlib/version.py +3 -0
  44. synapse/lib/stormlib/xml.py +3 -0
  45. synapse/lib/stormlib/yaml.py +2 -0
  46. synapse/lib/stormtypes.py +250 -170
  47. synapse/lib/trigger.py +180 -4
  48. synapse/lib/types.py +1 -1
  49. synapse/lib/version.py +2 -2
  50. synapse/lib/view.py +55 -6
  51. synapse/models/inet.py +21 -6
  52. synapse/models/orgs.py +48 -2
  53. synapse/models/risk.py +126 -2
  54. synapse/models/syn.py +6 -0
  55. synapse/tests/files/stormpkg/badapidef.yaml +13 -0
  56. synapse/tests/files/stormpkg/storm/modules/apimod +10 -0
  57. synapse/tests/files/stormpkg/testpkg.yaml +23 -0
  58. synapse/tests/test_axon.py +7 -2
  59. synapse/tests/test_cortex.py +231 -35
  60. synapse/tests/test_lib_ast.py +138 -43
  61. synapse/tests/test_lib_autodoc.py +1 -1
  62. synapse/tests/test_lib_modelrev.py +9 -0
  63. synapse/tests/test_lib_node.py +55 -0
  64. synapse/tests/test_lib_storm.py +14 -1
  65. synapse/tests/test_lib_stormhttp.py +65 -6
  66. synapse/tests/test_lib_stormlib_auth.py +12 -3
  67. synapse/tests/test_lib_stormlib_cortex.py +1327 -0
  68. synapse/tests/test_lib_stormlib_iters.py +116 -0
  69. synapse/tests/test_lib_stormlib_stats.py +187 -0
  70. synapse/tests/test_lib_stormlib_storm.py +8 -0
  71. synapse/tests/test_lib_stormsvc.py +24 -1
  72. synapse/tests/test_lib_stormtypes.py +124 -69
  73. synapse/tests/test_lib_trigger.py +315 -0
  74. synapse/tests/test_lib_view.py +1 -2
  75. synapse/tests/test_model_base.py +26 -0
  76. synapse/tests/test_model_inet.py +22 -0
  77. synapse/tests/test_model_orgs.py +28 -0
  78. synapse/tests/test_model_risk.py +73 -0
  79. synapse/tests/test_tools_autodoc.py +25 -0
  80. synapse/tests/test_tools_genpkg.py +9 -3
  81. synapse/tests/utils.py +39 -0
  82. synapse/tools/autodoc.py +42 -2
  83. {synapse-2.152.0.dist-info → synapse-2.154.0.dist-info}/METADATA +2 -2
  84. {synapse-2.152.0.dist-info → synapse-2.154.0.dist-info}/RECORD +87 -79
  85. {synapse-2.152.0.dist-info → synapse-2.154.0.dist-info}/WHEEL +1 -1
  86. {synapse-2.152.0.dist-info → synapse-2.154.0.dist-info}/LICENSE +0 -0
  87. {synapse-2.152.0.dist-info → synapse-2.154.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,116 @@
1
+ from unittest import mock
2
+
3
+ import synapse.exc as s_exc
4
+
5
+ import synapse.tests.utils as s_test
6
+
7
+ import synapse.lib.stormlib.iters as s_stormlib_iters
8
+
9
+ class StormLibItersTest(s_test.SynTest):
10
+
11
+ async def test_stormlib_iters_zip(self):
12
+
13
+ async with self.getTestCore() as core:
14
+
15
+ async with s_test.matchContexts(self):
16
+
17
+ q = '''
18
+ for $item in $lib.iters.zip((1,2,3), (4,5,6), (7,8,9)) {
19
+ $lib.print($item)
20
+ }
21
+ '''
22
+ msgs = await core.stormlist(q)
23
+ self.stormIsInPrint("['1', '4', '7']", msgs)
24
+ self.stormIsInPrint("['2', '5', '8']", msgs)
25
+ self.stormIsInPrint("['3', '6', '9']", msgs)
26
+
27
+ q = '''
28
+ for $item in $lib.iters.zip((1,2,3), (4,5,6), (7,8)) {
29
+ $lib.print($item)
30
+ }
31
+ '''
32
+ msgs = await core.stormlist(q)
33
+ self.stormIsInPrint("['1', '4', '7']", msgs)
34
+ self.stormIsInPrint("['2', '5', '8']", msgs)
35
+ self.stormNotInPrint("['3', '6']", msgs)
36
+
37
+ q = '''
38
+ function nodes() {
39
+ [ it:dev:str=4 it:dev:str=5 it:dev:str=6 ]
40
+ }
41
+ function emitter() {
42
+ emit 7
43
+ emit 8
44
+ emit 9
45
+ }
46
+ for ($a, $b, $c) in $lib.iters.zip((1,2,3), $nodes(), $emitter()) {
47
+ $lib.print(($a, $b.0.repr(), $c))
48
+ }
49
+ '''
50
+ msgs = await core.stormlist(q)
51
+ self.stormIsInPrint("['1', '4', '7']", msgs)
52
+ self.stormIsInPrint("['2', '5', '8']", msgs)
53
+ self.stormIsInPrint("['3', '6', '9']", msgs)
54
+
55
+ q = '''
56
+ function nodes() {
57
+ [ it:dev:str=4 it:dev:str=5 it:dev:str=6 ]
58
+ }
59
+ function emitter() {
60
+ emit 7
61
+ emit 8
62
+ emit 9
63
+ }
64
+ for ($a, $b, $c) in $lib.iters.zip((1,2,3), $nodes(), $emitter()) {
65
+ $lib.print(($a, $b.0.repr(), $c))
66
+ $lib.raise(foo, bar)
67
+ }
68
+ '''
69
+ msgs = await core.stormlist(q)
70
+ self.stormIsInPrint("['1', '4', '7']", msgs)
71
+ self.stormNotInPrint("['2', '5', '8']", msgs)
72
+ self.stormNotInPrint("['3', '6', '9']", msgs)
73
+
74
+ q = '''
75
+ function nodes() {
76
+ [ it:dev:str=4 it:dev:str=5 it:dev:str=6]
77
+ }
78
+ function emitter() {
79
+ emit 7
80
+ $lib.raise(foo, bar)
81
+ emit 8
82
+ emit 9
83
+ }
84
+ for ($a, $b, $c) in $lib.iters.zip((1,2,3), $nodes(), $emitter()) {
85
+ $lib.print(($a, $b.0.repr(), $c))
86
+ }
87
+ '''
88
+ msgs = await core.stormlist(q)
89
+ self.stormIsInPrint("['1', '4', '7']", msgs)
90
+ self.stormNotInPrint("['2', '5', '8']", msgs)
91
+ self.stormNotInPrint("['3', '6', '9']", msgs)
92
+
93
+ err = "$lib.iters.zip() encountered errors in 1 iterators during iteration: (foo: bar)"
94
+ self.stormIsInErr(err, msgs)
95
+
96
+ async def boom(self, genr):
97
+ print(newp)
98
+ yield True
99
+
100
+ with mock.patch.object(s_stormlib_iters.LibIters, 'enum', boom):
101
+ q = '''
102
+ function e1() {
103
+ $lib.raise(foo, bar)
104
+ }
105
+ function e2() {
106
+ $lib.print($newp)
107
+ }
108
+ for ($a, $b, $c) in $lib.iters.zip($e1(), $e2(), $lib.iters.enum($e2())) {
109
+ }
110
+ '''
111
+ msgs = await core.stormlist(q)
112
+ err = "$lib.iters.zip() encountered errors in 3 iterators during iteration: " \
113
+ "(foo: bar), " \
114
+ "(NoSuchVar: Missing variable: newp), " \
115
+ "(NameError: name 'newp' is not defined)"
116
+ self.stormIsInErr(err, msgs)
@@ -0,0 +1,187 @@
1
+ import synapse.exc as s_exc
2
+ import synapse.tests.utils as s_test
3
+
4
+ import synapse.lib.stormlib.stats as s_stormlib_stats
5
+
6
+ chartnorm = '''
7
+ 40 | 5 | ##################################################
8
+ 30 | 4 | ########################################
9
+ 20 | 3 | ##############################
10
+ 10 | 2 | ####################
11
+ 0 | 1 | ##########
12
+ '''.strip()
13
+
14
+ chartrev = '''
15
+ 0 | 1 | ##########
16
+ 10 | 2 | ####################
17
+ 20 | 3 | ##############################
18
+ 30 | 4 | ########################################
19
+ 40 | 5 | ##################################################
20
+ '''.strip()
21
+
22
+ chartsize = '''
23
+ 40 | 5 | ##################################################
24
+ 30 | 4 | ########################################
25
+ 20 | 3 | ##############################
26
+ '''.strip()
27
+
28
+ chartsizerev = '''
29
+ 0 | 1 | ##########
30
+ 10 | 2 | ####################
31
+ 20 | 3 | ##############################
32
+ '''.strip()
33
+
34
+ chartwidth = '''
35
+ 40 | 5 | ##########
36
+ 30 | 4 | ########
37
+ 20 | 3 | ######
38
+ 10 | 2 | ####
39
+ 0 | 1 | ##
40
+ '''.strip()
41
+
42
+ chartlabelwidth = '''
43
+ 4 | 5 | ##################################################
44
+ 3 | 4 | ########################################
45
+ 2 | 3 | ##############################
46
+ 1 | 2 | ####################
47
+ 0 | 1 | ##########
48
+ '''.strip()
49
+
50
+ chartchar = '''
51
+ 40 | 5 | ++++++++++++++++++++++++++++++++++++++++++++++++++
52
+ 30 | 4 | ++++++++++++++++++++++++++++++++++++++++
53
+ 20 | 3 | ++++++++++++++++++++++++++++++
54
+ 10 | 2 | ++++++++++++++++++++
55
+ 0 | 1 | ++++++++++
56
+ '''.strip()
57
+
58
+
59
+ class StatsTest(s_test.SynTest):
60
+
61
+ async def test_stormlib_stats_countby(self):
62
+
63
+ async with self.getTestCore() as core:
64
+
65
+ q = '''
66
+ $i = (0)
67
+ for $x in $lib.range(5) {
68
+ for $y in $lib.range(($x + 1)) {
69
+ [ inet:ipv4=$i :asn=($x * 10) ]
70
+ $i = ($i + 1)
71
+ }
72
+ }
73
+ '''
74
+ await core.nodes(q)
75
+
76
+ msgs = await core.stormlist('inet:ipv4 | stats.countby :asn')
77
+ self.stormIsInPrint(chartnorm, msgs)
78
+
79
+ msgs = await core.stormlist('inet:ipv4 -> inet:asn | stats.countby')
80
+ self.stormIsInPrint(chartnorm, msgs)
81
+
82
+ msgs = await core.stormlist('inet:ipv4 | stats.countby :asn --reverse')
83
+ self.stormIsInPrint(chartrev, msgs)
84
+
85
+ msgs = await core.stormlist('inet:ipv4 | stats.countby :asn --size 3')
86
+ self.stormIsInPrint(chartsize, msgs)
87
+
88
+ msgs = await core.stormlist('inet:ipv4 | stats.countby :asn --size 3 --reverse')
89
+ self.stormIsInPrint(chartsizerev, msgs)
90
+
91
+ msgs = await core.stormlist(f'inet:ipv4 | stats.countby :asn --bar-width 10')
92
+ self.stormIsInPrint(chartwidth, msgs)
93
+
94
+ msgs = await core.stormlist(f'inet:ipv4 | stats.countby :asn --label-max-width 1')
95
+ self.stormIsInPrint(chartlabelwidth, msgs)
96
+
97
+ msgs = await core.stormlist('inet:ipv4 | stats.countby :asn --char "+"')
98
+ self.stormIsInPrint(chartchar, msgs)
99
+
100
+ msgs = await core.stormlist('stats.countby foo')
101
+ self.stormIsInPrint('No values to display!', msgs)
102
+
103
+ self.len(0, await core.nodes('inet:ipv4 | stats.countby :asn'))
104
+ self.len(15, await core.nodes('inet:ipv4 | stats.countby :asn --yield'))
105
+
106
+ with self.raises(s_exc.BadArg):
107
+ self.len(15, await core.nodes('inet:ipv4 | stats.countby :asn --label-max-width "-1"'))
108
+
109
+ with self.raises(s_exc.BadArg):
110
+ self.len(15, await core.nodes('inet:ipv4 | stats.countby :asn --bar-width "-1"'))
111
+
112
+ with self.raises(s_exc.BadArg):
113
+ self.len(15, await core.nodes('inet:ipv4 | stats.countby ({})'))
114
+
115
+ async def test_stormlib_stats_tally(self):
116
+
117
+ async with self.getTestCore() as core:
118
+
119
+ q = '''
120
+ $tally = $lib.stats.tally()
121
+
122
+ $tally.inc(foo)
123
+ $tally.inc(foo)
124
+
125
+ $tally.inc(bar)
126
+ $tally.inc(bar, 3)
127
+
128
+ for ($name, $valu) in $tally {
129
+ [ test:comp=($valu, $name) ]
130
+ }
131
+
132
+ $lib.print('tally: foo={foo} baz={baz}', foo=$tally.get(foo), baz=$tally.get(baz))
133
+ $lib.print('tally.len()={v}', v=$lib.len($tally))
134
+ '''
135
+ mesgs = await core.stormlist(q)
136
+ nodes = [m[1] for m in mesgs if m[0] == 'node']
137
+ self.len(2, nodes)
138
+ self.eq(nodes[0][0], ('test:comp', (2, 'foo')))
139
+ self.eq(nodes[1][0], ('test:comp', (4, 'bar')))
140
+ self.stormIsInPrint('tally: foo=2 baz=0', mesgs)
141
+ self.stormIsInPrint('tally.len()=2', mesgs)
142
+
143
+ q = '''
144
+ $tally = $lib.stats.tally()
145
+ $tally.inc(foo, 1)
146
+ $tally.inc(bar, 2)
147
+ $tally.inc(baz, 3)
148
+ return($tally.sorted())
149
+ '''
150
+ vals = await core.callStorm(q)
151
+ self.eq(vals, [('foo', 1), ('bar', 2), ('baz', 3)])
152
+
153
+ q = '''
154
+ $tally = $lib.stats.tally()
155
+ $tally.inc(foo, 1)
156
+ $tally.inc(bar, 2)
157
+ $tally.inc(baz, 3)
158
+ return($tally.sorted(reverse=$lib.true))
159
+ '''
160
+ vals = await core.callStorm(q)
161
+ self.eq(vals, [('baz', 3), ('bar', 2), ('foo', 1)])
162
+
163
+ q = '''
164
+ $tally = $lib.stats.tally()
165
+ $tally.inc(foo, 1)
166
+ $tally.inc(bar, 2)
167
+ $tally.inc(baz, 3)
168
+ return($tally.sorted(byname=$lib.true))
169
+ '''
170
+ vals = await core.callStorm(q)
171
+ self.eq(vals, [('bar', 2), ('baz', 3), ('foo', 1)])
172
+
173
+ q = '''
174
+ $tally = $lib.stats.tally()
175
+ $tally.inc(foo, 1)
176
+ $tally.inc(bar, 2)
177
+ $tally.inc(baz, 3)
178
+ return($tally.sorted(byname=$lib.true, reverse=$lib.true))
179
+ '''
180
+ vals = await core.callStorm(q)
181
+ self.eq(vals, [('foo', 1), ('baz', 3), ('bar', 2)])
182
+
183
+ tally = s_stormlib_stats.StatTally()
184
+ await tally.inc('foo')
185
+
186
+ async for (name, valu) in tally:
187
+ self.eq((name, valu), ('foo', 1))
@@ -28,3 +28,11 @@ class LibStormTest(s_test.SynTest):
28
28
 
29
29
  # for coverage of forked call...
30
30
  self.nn(s_parser.parseEval('woot'))
31
+
32
+ # Readonly functionality is sane
33
+ msgs = await core.stormlist('$lib.print($lib.storm.eval( "{$lib.print(wow)}" ))')
34
+ self.stormIsInPrint('wow', msgs)
35
+ self.stormIsInPrint('$lib.null', msgs)
36
+
37
+ with self.raises(s_exc.IsReadOnly):
38
+ await core.callStorm('$lib.storm.eval( "{$lib.auth.users.add(readonly)}" )', opts={'readonly': True})
@@ -317,7 +317,30 @@ class StormvarService(s_cell.CellApi, s_stormsvc.StormSvc):
317
317
  $lib.print('my foo var is {f}', f=$fooz)
318
318
  ''',
319
319
  },
320
- )
320
+ ),
321
+ 'modules': (
322
+ {
323
+ 'name': 'testmod',
324
+ 'storm': '',
325
+ },
326
+ {
327
+ 'name': 'apimod',
328
+ 'storm': 'function status() { return($lib.true) }',
329
+ 'apidefs': (
330
+ {
331
+ 'name': 'status',
332
+ 'desc': 'Status of the foo.',
333
+ 'type': {
334
+ 'type': 'function',
335
+ 'returns': {
336
+ 'type': 'boolean',
337
+ 'desc': 'Where foo is ok',
338
+ },
339
+ },
340
+ },
341
+ ),
342
+ },
343
+ ),
321
344
  },
322
345
  )
323
346
 
@@ -613,6 +613,14 @@ class StormTypesTest(s_test.SynTest):
613
613
  'name': 'test',
614
614
  'storm': '$valu=$modconf.valu function getvalu() { return($valu) }',
615
615
  'modconf': {'valu': 'foo'},
616
+ },
617
+ {
618
+ 'name': 'test.danger',
619
+ 'storm': '''
620
+ init { $src=$lib.null }
621
+ function genSrc() { if $src { return ($src) } [meta:source=(s1,)] $src=$node return ($node) }
622
+ $genSrc()
623
+ '''
616
624
  }
617
625
  ],
618
626
  'commands': [
@@ -735,9 +743,13 @@ class StormTypesTest(s_test.SynTest):
735
743
 
736
744
  await core.callStorm('$test = $lib.import(test) $test.modconf.valu=bar')
737
745
  self.eq('foo', await core.callStorm('return($lib.import(test).getvalu())'))
746
+ self.eq('foo', await core.callStorm('return($lib.import(test).getvalu())', opts={'readonly': True}))
747
+
748
+ with self.raises(s_exc.IsReadOnly):
749
+ await core.callStorm('return($lib.import(test.danger).src)', opts={'readonly': True})
738
750
 
739
751
  mods = await core.getStormMods()
740
- self.len(1, mods)
752
+ self.len(2, mods)
741
753
  mods['test']['modconf']['valu'] = 'bar'
742
754
  mods = await core.getStormMods()
743
755
  self.eq('foo', mods['test']['modconf']['valu'])
@@ -1181,6 +1193,24 @@ class StormTypesTest(s_test.SynTest):
1181
1193
  '''
1182
1194
  self.eq(2, await core.callStorm(q))
1183
1195
 
1196
+ q = '''
1197
+ $q=${ return( $lib.auth.users.byname(root).name ) }
1198
+ return ( $q.exec() )
1199
+ '''
1200
+ self.eq('root', await core.callStorm(q, opts={'readonly': True}))
1201
+
1202
+ q = '''
1203
+ $q=${ test:int=1 }
1204
+ return ( $q.size() )
1205
+ '''
1206
+ self.eq(1, await core.callStorm(q, opts={'readonly': True}))
1207
+
1208
+ with self.raises(s_exc.IsReadOnly):
1209
+ await core.callStorm('$foo=${ [test:str=readonly] } return ( $foo.exec() )', opts={'readonly': True})
1210
+
1211
+ with self.raises(s_exc.IsReadOnly):
1212
+ await core.callStorm('$foo=${ [test:str=readonly] } return( $foo.size() )', opts={'readonly': True})
1213
+
1184
1214
  async def test_storm_lib_node(self):
1185
1215
  async with self.getTestCore() as core:
1186
1216
  nodes = await core.nodes('[ test:str=woot :tick=2001] [ test:int=$node.isform(test:str) ] +test:int')
@@ -1514,6 +1544,72 @@ class StormTypesTest(s_test.SynTest):
1514
1544
  q = '$foo=$lib.text(foo) $bar=$lib.text(bar) $v=($foo, aString, $bar,) $v.sort() return ($v)'
1515
1545
  await core.callStorm(q)
1516
1546
 
1547
+ q = '$l = (1, 2, (3), 4, 1, (3), 3, asdf) return ( $l.unique() )'
1548
+ self.eq(['1', '2', 3, '4', '3', 'asdf'], await core.callStorm(q))
1549
+
1550
+ q = '$a=$lib.text(hehe) $b=$lib.text(haha) $c=$lib.text(hehe) $foo=($a, $b, $c) return ($foo.unique())'
1551
+ self.eq(['hehe', 'haha'], await core.callStorm(q))
1552
+
1553
+ await core.addUser('lowuser1')
1554
+ await core.addUser('lowuser2')
1555
+ q = '''
1556
+ $a=$lib.auth.users.byname(lowuser1) $b=$lib.auth.users.byname(lowuser2) $r=$lib.auth.users.byname(root)
1557
+ $l = ($a, $r, $b, $b, $a ) $l2 = $l.unique() $l3 = ()
1558
+ for $user in $l2 {
1559
+ $l3.append($user.name)
1560
+ }
1561
+ return ( $l3 )'''
1562
+ self.eq(['lowuser1', 'root', 'lowuser2'], await core.callStorm(q))
1563
+
1564
+ q = '$l=(1, 2, 3, 3, $lib.queue) return ( $lib.len($l.unique()) )'
1565
+ self.eq(4, await core.callStorm(q))
1566
+
1567
+ # funcs are different class instances here
1568
+ q = '$l = (1, 2, 2, $lib.inet.http.get, $lib.inet.http.get) return ($lib.len($l.unique()))'
1569
+ self.eq(4, await core.callStorm(q))
1570
+
1571
+ # funcs are the same class instance
1572
+ q = '$hehe = $lib.inet.http $l = (1, 2, 2, $hehe.get, $hehe.get) return ($lib.len($l.unique()))'
1573
+ self.eq(3, await core.callStorm(q))
1574
+
1575
+ q = '''
1576
+ function foo() {}
1577
+ function bar() {}
1578
+ $l = ($foo, $bar)
1579
+ return ($lib.len($l.unique()))
1580
+ '''
1581
+ self.eq(2, await core.callStorm(q))
1582
+
1583
+ q = '''
1584
+ function foo() {}
1585
+ function bar() {}
1586
+ $l = ($bar, $foo, $bar)
1587
+ return ($lib.len($l.unique()))
1588
+ '''
1589
+ self.eq(2, await core.callStorm(q))
1590
+
1591
+ q = '''
1592
+ $q1 = $lib.queue.gen(hehe)
1593
+ $q2 = $lib.queue.get(hehe)
1594
+ $l = ($q1, $q2)
1595
+ return ( $lib.len($l.unique()) ) '''
1596
+ self.eq(1, await core.callStorm(q))
1597
+
1598
+ q = '''
1599
+ $q1 = $lib.queue.gen(hehe)
1600
+ $q2 = $lib.queue.get(hehe)
1601
+ $l = ($q1, $q2, $q2)
1602
+ return ( $lib.len($l.unique()) ) '''
1603
+ self.eq(1, await core.callStorm(q))
1604
+
1605
+ q = '''
1606
+ $q1 = $lib.queue.gen(hehe)
1607
+ $q2 = $lib.queue.get(hehe)
1608
+ $q3 = $lib.queue.get(hehe)
1609
+ $l = ($q1, $q2, $q3)
1610
+ return ( $lib.len($l.unique()) ) '''
1611
+ self.eq(1, await core.callStorm(q))
1612
+
1517
1613
  # Python Tuples can be treated like a List object for accessing via data inside of.
1518
1614
  q = '[ test:comp=(10,lol) ] $x=$node.ndef().index(1).index(1) [ test:str=$x ]'
1519
1615
  nodes = await core.nodes(q)
@@ -3265,74 +3361,6 @@ class StormTypesTest(s_test.SynTest):
3265
3361
  errs = [m for m in msgs if m[0] == 'err']
3266
3362
  self.len(0, errs)
3267
3363
 
3268
- async def test_lib_stormtypes_stats(self):
3269
-
3270
- async with self.getTestCore() as core:
3271
-
3272
- q = '''
3273
- $tally = $lib.stats.tally()
3274
-
3275
- $tally.inc(foo)
3276
- $tally.inc(foo)
3277
-
3278
- $tally.inc(bar)
3279
- $tally.inc(bar, 3)
3280
-
3281
- for ($name, $valu) in $tally {
3282
- [ test:comp=($valu, $name) ]
3283
- }
3284
-
3285
- $lib.print('tally: foo={foo} baz={baz}', foo=$tally.get(foo), baz=$tally.get(baz))
3286
- $lib.print('tally.len()={v}', v=$lib.len($tally))
3287
- '''
3288
- mesgs = await core.stormlist(q)
3289
- nodes = [m[1] for m in mesgs if m[0] == 'node']
3290
- self.len(2, nodes)
3291
- self.eq(nodes[0][0], ('test:comp', (2, 'foo')))
3292
- self.eq(nodes[1][0], ('test:comp', (4, 'bar')))
3293
- self.stormIsInPrint('tally: foo=2 baz=0', mesgs)
3294
- self.stormIsInPrint('tally.len()=2', mesgs)
3295
-
3296
- q = '''
3297
- $tally = $lib.stats.tally()
3298
- $tally.inc(foo, 1)
3299
- $tally.inc(bar, 2)
3300
- $tally.inc(baz, 3)
3301
- return($tally.sorted())
3302
- '''
3303
- vals = await core.callStorm(q)
3304
- self.eq(vals, [('foo', 1), ('bar', 2), ('baz', 3)])
3305
-
3306
- q = '''
3307
- $tally = $lib.stats.tally()
3308
- $tally.inc(foo, 1)
3309
- $tally.inc(bar, 2)
3310
- $tally.inc(baz, 3)
3311
- return($tally.sorted(reverse=$lib.true))
3312
- '''
3313
- vals = await core.callStorm(q)
3314
- self.eq(vals, [('baz', 3), ('bar', 2), ('foo', 1)])
3315
-
3316
- q = '''
3317
- $tally = $lib.stats.tally()
3318
- $tally.inc(foo, 1)
3319
- $tally.inc(bar, 2)
3320
- $tally.inc(baz, 3)
3321
- return($tally.sorted(byname=$lib.true))
3322
- '''
3323
- vals = await core.callStorm(q)
3324
- self.eq(vals, [('bar', 2), ('baz', 3), ('foo', 1)])
3325
-
3326
- q = '''
3327
- $tally = $lib.stats.tally()
3328
- $tally.inc(foo, 1)
3329
- $tally.inc(bar, 2)
3330
- $tally.inc(baz, 3)
3331
- return($tally.sorted(byname=$lib.true, reverse=$lib.true))
3332
- '''
3333
- vals = await core.callStorm(q)
3334
- self.eq(vals, [('foo', 1), ('baz', 3), ('bar', 2)])
3335
-
3336
3364
  async def test_storm_lib_layer(self):
3337
3365
 
3338
3366
  async with self.getTestCoreAndProxy() as (core, prox):
@@ -3533,10 +3561,18 @@ class StormTypesTest(s_test.SynTest):
3533
3561
  layrs0 = (layr0.iden, ldef1.get('iden'))
3534
3562
  layrs1 = (ldef1.get('iden'), layr0.iden)
3535
3563
 
3564
+ # fork the default view to test editing the root view layers
3565
+ fork00 = await core.callStorm('return($lib.view.get().fork().iden)')
3566
+ self.eq(2, await core.callStorm('return($lib.view.get().layers.size())', opts={'view': fork00}))
3567
+
3536
3568
  await core.callStorm('$lib.view.get().set(layers, $layers)', opts={'vars': {'layers': layrs0}})
3537
3569
  ldefs = await core.callStorm('return($lib.view.get().get(layers))')
3538
3570
  self.eq(layrs0, [x.get('iden') for x in ldefs])
3539
3571
 
3572
+ layers = await core.callStorm('return($lib.view.get().layers)', opts={'view': fork00})
3573
+ self.eq(layrs0, [layr['iden'] for layr in layers][-2:])
3574
+ self.len(3, layers)
3575
+
3540
3576
  await core.callStorm('$lib.view.get().set(layers, $layers)', opts={'vars': {'layers': layrs1}})
3541
3577
  ldefs = await core.callStorm('return($lib.view.get().get(layers))')
3542
3578
  self.eq(layrs1, [x.get('iden') for x in ldefs])
@@ -6289,6 +6325,25 @@ words\tword\twrd'''
6289
6325
  'size:bytes': 651,
6290
6326
  }, await core.callStorm('return($lib.axon.metrics())'))
6291
6327
 
6328
+ bin_buf = b'\xbb/$\xc0A\xf1\xbf\xbc\x00_\x82v4\xf6\xbd\x1b'
6329
+ binsize, bin256 = await core.axon.put(bin_buf)
6330
+
6331
+ opts = {'vars': {'sha256': s_common.ehex(bin256)}}
6332
+ with self.raises(s_exc.BadDataValu):
6333
+ self.eq('', await core.callStorm('''
6334
+ $items = $lib.list()
6335
+ for $item in $lib.axon.readlines($sha256, errors=$lib.null) {
6336
+ $items.append($item)
6337
+ }
6338
+ return($items)
6339
+ ''', opts=opts))
6340
+
6341
+ self.eq(('/$A\x00_v4\x1b',), await core.callStorm('''
6342
+ $items = $lib.list()
6343
+ for $item in $lib.axon.readlines($sha256, errors=ignore) { $items.append($item) }
6344
+ return($items)
6345
+ ''', opts=opts))
6346
+
6292
6347
  async def test_storm_lib_axon_perms(self):
6293
6348
 
6294
6349
  async with self.getTestCore() as core: