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
synapse/lib/storm.py CHANGED
@@ -45,6 +45,8 @@ Notes:
45
45
  * node:add
46
46
  * node:del
47
47
  * prop:set
48
+ * edge:add
49
+ * edge:del
48
50
 
49
51
  When condition is tag:add or tag:del, you may optionally provide a form name
50
52
  to restrict the trigger to fire only on tags added or deleted from nodes of
@@ -56,6 +58,10 @@ Simple one level tag globbing is supported, only at the end after a period,
56
58
  that is aka.* matches aka.foo and aka.bar but not aka.foo.bar. aka* is not
57
59
  supported.
58
60
 
61
+ When the condition is edge:add or edge:del, you may optionally provide a
62
+ form name or a destination form name to only fire on edges added or deleted
63
+ from nodes of those forms.
64
+
59
65
  Examples:
60
66
  # Adds a tag to every inet:ipv4 added
61
67
  trigger.add node:add --form inet:ipv4 --query {[ +#mytag ]}
@@ -65,6 +71,13 @@ Examples:
65
71
 
66
72
  # Adds a tag #todo to every inet:ipv4 as it is tagged #aka
67
73
  trigger.add tag:add --form inet:ipv4 --tag aka --query {[ +#todo ]}
74
+
75
+ # Adds a tag #todo to the N1 node of every refs edge add
76
+ trigger.add edge:add --verb refs --query {[ +#todo ]}
77
+
78
+ # Adds a tag #todo to the N1 node of every seen edge delete, provided that
79
+ # both nodes are of form file:bytes
80
+ trigger.add edge:del --verb seen --form file:bytes --n2form file:bytes --query {[ +#todo ]}
68
81
  '''
69
82
 
70
83
  addcrondescr = '''
@@ -307,6 +320,10 @@ reqValidPkgdef = s_config.getJsValidator({
307
320
  'name': {'type': 'string'},
308
321
  'storm': {'type': 'string'},
309
322
  'modconf': {'type': 'object'},
323
+ 'apidefs': {
324
+ 'type': ['array', 'null'],
325
+ 'items': {'$ref': '#/definitions/apidef'},
326
+ },
310
327
  'asroot': {'type': 'boolean'},
311
328
  'asroot:perms': {'type': 'array',
312
329
  'items': {'type': 'array',
@@ -316,6 +333,67 @@ reqValidPkgdef = s_config.getJsValidator({
316
333
  'additionalProperties': True,
317
334
  'required': ['name', 'storm']
318
335
  },
336
+ 'apidef': {
337
+ 'type': 'object',
338
+ 'properties': {
339
+ 'name': {'type': 'string'},
340
+ 'desc': {'type': 'string'},
341
+ 'type': {
342
+ 'type': 'object',
343
+ 'properties': {
344
+ 'type': {
345
+ 'type': 'string',
346
+ 'enum': ['function']
347
+ },
348
+ 'args': {
349
+ 'type': 'array',
350
+ 'items': {'$ref': '#/definitions/apiarg'},
351
+ },
352
+ 'returns': {
353
+ 'type': 'object',
354
+ 'properties': {
355
+ 'name': {
356
+ 'type': 'string',
357
+ 'enum': ['yields'],
358
+ },
359
+ 'desc': {'type': 'string'},
360
+ 'type': {
361
+ 'oneOf': [
362
+ {'$ref': '#/definitions/apitype'},
363
+ {'type': 'array', 'items': {'$ref': '#/definitions/apitype'}},
364
+ ],
365
+ },
366
+ },
367
+ 'additionalProperties': False,
368
+ 'required': ['type', 'desc']
369
+ },
370
+ },
371
+ 'additionalProperties': False,
372
+ 'required': ['type', 'returns'],
373
+ },
374
+ },
375
+ 'additionalProperties': False,
376
+ 'required': ['name', 'desc', 'type']
377
+ },
378
+ 'apiarg': {
379
+ 'type': 'object',
380
+ 'properties': {
381
+ 'name': {'type': 'string'},
382
+ 'desc': {'type': 'string'},
383
+ 'type': {
384
+ 'oneOf': [
385
+ {'$ref': '#/definitions/apitype'},
386
+ {'type': 'array', 'items': {'$ref': '#/definitions/apitype'}},
387
+ ],
388
+ },
389
+ 'default': {'type': ['boolean', 'integer', 'string', 'null']},
390
+ },
391
+ 'additionalProperties': False,
392
+ 'required': ['name', 'desc', 'type']
393
+ },
394
+ 'apitype': {
395
+ 'type': 'string',
396
+ },
319
397
  'command': {
320
398
  'type': 'object',
321
399
  'properties': {
@@ -999,6 +1077,8 @@ stormcmds = (
999
1077
  ('--form', {'help': 'Form to fire on.'}),
1000
1078
  ('--tag', {'help': 'Tag to fire on.'}),
1001
1079
  ('--prop', {'help': 'Property to fire on.'}),
1080
+ ('--verb', {'help': 'Edge verb to fire on.'}),
1081
+ ('--n2form', {'help': 'The form of the n2 node to fire on.'}),
1002
1082
  ('--query', {'help': 'Query for the trigger to execute.', 'required': True,
1003
1083
  'dest': 'storm', }),
1004
1084
  ('--async', {'default': False, 'action': 'store_true',
@@ -2112,25 +2192,25 @@ class Runtime(s_base.Base):
2112
2192
  try:
2113
2193
  with s_provenance.claim('storm', q=self.query.text, user=self.user.iden):
2114
2194
 
2115
- nodegenr = self.query.iterNodePaths(self, genr=genr)
2116
- nodegenr, empty = await s_ast.pullone(nodegenr)
2195
+ async with contextlib.aclosing(self.query.iterNodePaths(self, genr=genr)) as nodegenr:
2196
+ nodegenr, empty = await s_ast.pullone(nodegenr)
2117
2197
 
2118
- if empty:
2119
- return
2198
+ if empty:
2199
+ return
2120
2200
 
2121
- rules = self.opts.get('graph')
2122
- if rules not in (False, None):
2123
- if rules is True:
2124
- rules = {'degrees': None, 'refs': True}
2125
- elif isinstance(rules, str):
2126
- rules = await self.snap.core.getStormGraph(rules)
2201
+ rules = self.opts.get('graph')
2202
+ if rules not in (False, None):
2203
+ if rules is True:
2204
+ rules = {'degrees': None, 'refs': True}
2205
+ elif isinstance(rules, str):
2206
+ rules = await self.snap.core.getStormGraph(rules)
2127
2207
 
2128
- subgraph = s_ast.SubGraph(rules)
2129
- nodegenr = subgraph.run(self, nodegenr)
2208
+ subgraph = s_ast.SubGraph(rules)
2209
+ nodegenr = subgraph.run(self, nodegenr)
2130
2210
 
2131
- async for item in nodegenr:
2132
- self.tick()
2133
- yield item
2211
+ async for item in nodegenr:
2212
+ self.tick()
2213
+ yield item
2134
2214
 
2135
2215
  except RecursionError:
2136
2216
  mesg = 'Maximum Storm pipeline depth exceeded.'
@@ -2147,7 +2227,7 @@ class Runtime(s_base.Base):
2147
2227
 
2148
2228
  view = snap.core.views.get(viewiden)
2149
2229
  if view is None:
2150
- raise s_exc.NoSuchView(iden=viewiden)
2230
+ raise s_exc.NoSuchView(mesg=f'No such view iden={viewiden}', iden=viewiden)
2151
2231
 
2152
2232
  self.user.confirm(('view', 'read'), gateiden=viewiden)
2153
2233
  snap = await view.snap(self.user)
@@ -2438,8 +2518,7 @@ class Parser:
2438
2518
 
2439
2519
  if nargs == '?':
2440
2520
 
2441
- # ? will have an implicit default value of None
2442
- opts.setdefault(dest, None)
2521
+ opts.setdefault(dest, argdef.get('default'))
2443
2522
 
2444
2523
  if todo and not self._is_opt(todo[0]):
2445
2524
 
@@ -3450,7 +3529,7 @@ class CopyToCmd(Cmd):
3450
3529
 
3451
3530
  view = runt.snap.core.getView(iden)
3452
3531
  if view is None:
3453
- raise s_exc.NoSuchView(mesg=f'No such view: {iden}')
3532
+ raise s_exc.NoSuchView(mesg=f'No such view: {iden=}', iden=iden)
3454
3533
 
3455
3534
  runt.confirm(('view', 'read'), gateiden=view.iden)
3456
3535
 
@@ -107,7 +107,7 @@ TerminalPygMap = {
107
107
  '_LEFTPIVOT': p_t.Punctuation,
108
108
  '_LPARNOSPACE': p_t.Punctuation,
109
109
  '_MATCHHASH': p_t.Punctuation,
110
- '_MATCHHASHSPACE': p_t.Punctuation,
110
+ '_MATCHHASHWILD': p_t.Punctuation,
111
111
  '_RETURN': p_t.Keyword,
112
112
  '_REVERSE': p_t.Keyword,
113
113
  '_RIGHTJOIN': p_t.Punctuation,
synapse/lib/stormhttp.py CHANGED
@@ -250,6 +250,10 @@ class LibHttp(s_stormtypes.Lib):
250
250
  'returns': {'type': 'str', 'desc': 'The reason phrase for the status code.', }}},
251
251
  )
252
252
  _storm_lib_path = ('inet', 'http')
253
+ _storm_lib_perms = (
254
+ {'perm': ('storm', 'lib', 'inet', 'http', 'proxy'), 'gate': 'cortex',
255
+ 'desc': 'Permits a user to specify the proxy used with `$lib.inet.http` APIs.'},
256
+ )
253
257
 
254
258
  def getObjLocals(self):
255
259
  return {
@@ -270,14 +274,17 @@ class LibHttp(s_stormtypes.Lib):
270
274
  return {str(k): str(v) for k, v in item.items()}
271
275
  return item
272
276
 
277
+ @s_stormtypes.stormfunc(readonly=True)
273
278
  async def urlencode(self, text):
274
279
  text = await s_stormtypes.tostr(text)
275
280
  return urllib.parse.quote_plus(text)
276
281
 
282
+ @s_stormtypes.stormfunc(readonly=True)
277
283
  async def urldecode(self, text):
278
284
  text = await s_stormtypes.tostr(text)
279
285
  return urllib.parse.unquote_plus(text)
280
286
 
287
+ @s_stormtypes.stormfunc(readonly=True)
281
288
  async def codereason(self, code):
282
289
  code = await s_stormtypes.toint(code)
283
290
  return s_common.httpcodereason(code)
@@ -310,8 +317,8 @@ class LibHttp(s_stormtypes.Lib):
310
317
 
311
318
  sock = await WebSocket.anit()
312
319
 
313
- if proxy is not None and not self.runt.isAdmin():
314
- raise s_exc.AuthDeny(mesg=s_exc.proxy_admin_mesg)
320
+ if proxy is not None:
321
+ self.runt.confirm(('storm', 'lib', 'inet', 'http', 'proxy'))
315
322
 
316
323
  if proxy is None:
317
324
  proxy = await self.runt.snap.core.getConfOpt('http:proxy')
@@ -374,8 +381,8 @@ class LibHttp(s_stormtypes.Lib):
374
381
 
375
382
  headers = self.strify(headers)
376
383
 
377
- if proxy is not None and not self.runt.isAdmin():
378
- raise s_exc.AuthDeny(mesg=s_exc.proxy_admin_mesg)
384
+ if proxy is not None:
385
+ self.runt.confirm(('storm', 'lib', 'inet', 'http', 'proxy'))
379
386
 
380
387
  if fields:
381
388
  if any(['sha256' in field for field in fields]):
@@ -63,6 +63,12 @@ stormcmds = (
63
63
 
64
64
  // Unlock the user "visi" and set their email to "visi@vertex.link"
65
65
  auth.user.mod visi --locked $lib.false --email visi@vertex.link
66
+
67
+ // Grant admin access to user visi for the current view
68
+ auth.user.mod visi --admin $lib.true --gate $lib.view.get().iden
69
+
70
+ // Revoke admin access to user visi for the current view
71
+ auth.user.mod visi --admin $lib.false --gate $lib.view.get().iden
66
72
  ''',
67
73
  'cmdargs': (
68
74
  ('username', {'type': 'str', 'help': 'The name of the user.'}),
@@ -70,6 +76,7 @@ stormcmds = (
70
76
  ('--email', {'type': 'str', 'help': 'The email address to set for the user.'}),
71
77
  ('--passwd', {'type': 'str', 'help': 'The new password for the user. This is best passed into the runtime as a variable.'}),
72
78
  ('--admin', {'type': 'bool', 'help': 'True to make the user and admin, false to remove their remove their admin status.'}),
79
+ ('--gate', {'type': 'str', 'help': 'The auth gate iden to grant or revoke admin status on. Use in conjunction with `--admin <bool>`.'}),
73
80
  ('--locked', {'type': 'bool', 'help': 'True to lock the user, false to unlock them.'}),
74
81
  ),
75
82
  'storm': '''
@@ -92,8 +99,15 @@ stormcmds = (
92
99
  $lib.print(`User ({$cmdopts.username}) locked status set to {$cmdopts.locked=1}.`)
93
100
  }
94
101
  if ($cmdopts.admin != $lib.null) {
95
- $user.setAdmin($cmdopts.admin)
96
- $lib.print(`User ({$cmdopts.username}) admin status set to {$cmdopts.admin=1}.`)
102
+ $user.setAdmin($cmdopts.admin, gateiden=$cmdopts.gate)
103
+ if $cmdopts.gate {
104
+ $lib.print(`User ({$cmdopts.username}) admin status set to {$cmdopts.admin=1} for auth gate {$cmdopts.gate}.`)
105
+ } else {
106
+ $lib.print(`User ({$cmdopts.username}) admin status set to {$cmdopts.admin=1}.`)
107
+ }
108
+ }
109
+ if ($cmdopts.gate != $lib.null and $cmdopts.admin = $lib.null) {
110
+ $lib.exit('Granting/revoking admin status on an auth gate, requires the use of `--admin <true|false>` also.')
97
111
  }
98
112
  } else {
99
113
  $lib.warn(`User ({$cmdopts.username}) not found!`)
@@ -50,6 +50,7 @@ class BackupLib(s_stormtypes.Lib):
50
50
  gatekeys = ((self.runt.user.iden, ('backup', 'run'), None),)
51
51
  return await self.dyncall('cortex', todo, gatekeys=gatekeys)
52
52
 
53
+ @s_stormtypes.stormfunc(readonly=True)
53
54
  async def _listBackups(self):
54
55
  todo = s_common.todo('getBackups')
55
56
  gatekeys = ((self.runt.user.iden, ('backup', 'list'), None),)
@@ -35,6 +35,7 @@ class BaseXLib(s_stormtypes.Lib):
35
35
  'decode': self.decode,
36
36
  }
37
37
 
38
+ @s_stormtypes.stormfunc(readonly=True)
38
39
  async def encode(self, byts, charset):
39
40
  if not isinstance(byts, bytes):
40
41
  raise s_exc.BadArg(mesg='$lib.basex.encode() requires a bytes argument.')
@@ -54,6 +55,7 @@ class BaseXLib(s_stormtypes.Lib):
54
55
 
55
56
  return ''.join(retn[::-1])
56
57
 
58
+ @s_stormtypes.stormfunc(readonly=True)
57
59
  async def decode(self, text, charset):
58
60
  text = await s_stormtypes.tostr(text)
59
61
  charset = await s_stormtypes.tostr(charset)
@@ -192,6 +192,7 @@ class CellLib(s_stormtypes.Lib):
192
192
 
193
193
  return curv
194
194
 
195
+ @s_stormtypes.stormfunc(readonly=True)
195
196
  async def _hotFixesCheck(self):
196
197
  if not self.runt.isAdmin():
197
198
  mesg = '$lib.cell.stormFixesCheck() requires admin privs.'
@@ -210,30 +211,35 @@ class CellLib(s_stormtypes.Lib):
210
211
 
211
212
  return dowork
212
213
 
214
+ @s_stormtypes.stormfunc(readonly=True)
213
215
  async def _getCellInfo(self):
214
216
  if not self.runt.isAdmin():
215
217
  mesg = '$lib.cell.getCellInfo() requires admin privs.'
216
218
  raise s_exc.AuthDeny(mesg=mesg, user=self.runt.user.iden, username=self.runt.user.name)
217
219
  return await self.runt.snap.core.getCellInfo()
218
220
 
221
+ @s_stormtypes.stormfunc(readonly=True)
219
222
  async def _getSystemInfo(self):
220
223
  if not self.runt.isAdmin():
221
224
  mesg = '$lib.cell.getSystemInfo() requires admin privs.'
222
225
  raise s_exc.AuthDeny(mesg=mesg, user=self.runt.user.iden, username=self.runt.user.name)
223
226
  return await self.runt.snap.core.getSystemInfo()
224
227
 
228
+ @s_stormtypes.stormfunc(readonly=True)
225
229
  async def _getBackupInfo(self):
226
230
  if not self.runt.isAdmin():
227
231
  mesg = '$lib.cell.getBackupInfo() requires admin privs.'
228
232
  raise s_exc.AuthDeny(mesg=mesg, user=self.runt.user.iden, username=self.runt.user.name)
229
233
  return await self.runt.snap.core.getBackupInfo()
230
234
 
235
+ @s_stormtypes.stormfunc(readonly=True)
231
236
  async def _getHealthCheck(self):
232
237
  if not self.runt.isAdmin():
233
238
  mesg = '$lib.cell.getHealthCheck() requires admin privs.'
234
239
  raise s_exc.AuthDeny(mesg=mesg, user=self.runt.user.iden, username=self.runt.user.name)
235
240
  return await self.runt.snap.core.getHealthCheck()
236
241
 
242
+ @s_stormtypes.stormfunc(readonly=True)
237
243
  async def _getMirrorUrls(self, name=None):
238
244
 
239
245
  if not self.runt.isAdmin():
@@ -265,6 +271,7 @@ class CellLib(s_stormtypes.Lib):
265
271
 
266
272
  return await self.runt.snap.core.trimNexsLog(consumers=consumers, timeout=timeout)
267
273
 
274
+ @s_stormtypes.stormfunc(readonly=True)
268
275
  async def _uptime(self, name=None):
269
276
 
270
277
  name = await s_stormtypes.tostr(name, noneok=True)
@@ -47,6 +47,7 @@ class Bzip2Lib(s_stormtypes.Lib):
47
47
  'un': self.un,
48
48
  }
49
49
 
50
+ @s_stormtypes.stormfunc(readonly=True)
50
51
  async def en(self, valu):
51
52
  valu = await s_stormtypes.toprim(valu)
52
53
  try:
@@ -103,6 +104,7 @@ class GzipLib(s_stormtypes.Lib):
103
104
  'un': self.un,
104
105
  }
105
106
 
107
+ @s_stormtypes.stormfunc(readonly=True)
106
108
  async def en(self, valu):
107
109
  valu = await s_stormtypes.toprim(valu)
108
110
  try:
@@ -159,6 +161,7 @@ class ZlibLib(s_stormtypes.Lib):
159
161
  'un': self.un,
160
162
  }
161
163
 
164
+ @s_stormtypes.stormfunc(readonly=True)
162
165
  async def en(self, valu):
163
166
  valu = await s_stormtypes.toprim(valu)
164
167
  try: