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
@@ -24,6 +24,7 @@ class EthereumLib(s_stormtypes.Lib):
24
24
  'eip55': self.eip55,
25
25
  }
26
26
 
27
+ @s_stormtypes.stormfunc(readonly=True)
27
28
  async def eip55(self, addr):
28
29
  addr = await s_stormtypes.tostr(addr)
29
30
  addr = addr.lower()
@@ -180,6 +180,7 @@ class GraphLib(s_stormtypes.Lib):
180
180
  gdef = await s_stormtypes.toprim(gdef)
181
181
  return await self.runt.snap.core.addStormGraph(gdef, user=self.runt.user)
182
182
 
183
+ @s_stormtypes.stormfunc(readonly=True)
183
184
  async def _methGraphGet(self, iden=None):
184
185
  iden = await s_stormtypes.tostr(iden, noneok=True)
185
186
  if iden is None:
@@ -201,6 +202,7 @@ class GraphLib(s_stormtypes.Lib):
201
202
 
202
203
  await self.runt.snap.core.modStormGraph(iden, info, user=self.runt.user)
203
204
 
205
+ @s_stormtypes.stormfunc(readonly=True)
204
206
  async def _methGraphList(self):
205
207
  projs = []
206
208
  async for proj in self.runt.snap.core.getStormGraphs(user=self.runt.user):
@@ -46,15 +46,19 @@ class LibHashes(s_stormtypes.Lib):
46
46
  'sha512': self._sha512,
47
47
  }
48
48
 
49
+ @s_stormtypes.stormfunc(readonly=True)
49
50
  async def _md5(self, byts):
50
51
  return hashlib.md5(byts, usedforsecurity=False).hexdigest()
51
52
 
53
+ @s_stormtypes.stormfunc(readonly=True)
52
54
  async def _sha1(self, byts):
53
55
  return hashlib.sha1(byts, usedforsecurity=False).hexdigest()
54
56
 
57
+ @s_stormtypes.stormfunc(readonly=True)
55
58
  async def _sha256(self, byts):
56
59
  return hashlib.sha256(byts).hexdigest()
57
60
 
61
+ @s_stormtypes.stormfunc(readonly=True)
58
62
  async def _sha512(self, byts):
59
63
  return hashlib.sha512(byts).hexdigest()
60
64
 
@@ -88,6 +92,7 @@ class LibHmac(s_stormtypes.Lib):
88
92
  'digest': self._digest,
89
93
  }
90
94
 
95
+ @s_stormtypes.stormfunc(readonly=True)
91
96
  async def _digest(self, key, mesg, alg='sha256') -> bytes:
92
97
  key = await s_stormtypes.toprim(key)
93
98
  if not isinstance(key, bytes):
@@ -74,11 +74,13 @@ class HexLib(s_stormtypes.Lib):
74
74
  'signext': self.signext,
75
75
  }
76
76
 
77
+ @s_stormtypes.stormfunc(readonly=True)
77
78
  async def encode(self, valu):
78
79
  if not isinstance(valu, bytes):
79
80
  raise s_exc.BadArg(mesg='$lib.hex.encode() requires a bytes argument.')
80
81
  return s_common.ehex(valu)
81
82
 
83
+ @s_stormtypes.stormfunc(readonly=True)
82
84
  async def decode(self, valu):
83
85
  valu = await s_stormtypes.tostr(valu)
84
86
  try:
@@ -86,6 +88,7 @@ class HexLib(s_stormtypes.Lib):
86
88
  except binascii.Error as e:
87
89
  raise s_exc.BadArg(mesg=f'$lib.hex.decode(): {e}')
88
90
 
91
+ @s_stormtypes.stormfunc(readonly=True)
89
92
  async def toint(self, valu, signed=False):
90
93
  valu = await s_stormtypes.tostr(valu)
91
94
  signed = await s_stormtypes.tobool(signed)
@@ -97,6 +100,7 @@ class HexLib(s_stormtypes.Lib):
97
100
 
98
101
  return int.from_bytes(byts, 'big', signed=signed)
99
102
 
103
+ @s_stormtypes.stormfunc(readonly=True)
100
104
  async def fromint(self, valu, length, signed=False):
101
105
  valu = await s_stormtypes.toint(valu)
102
106
  length = await s_stormtypes.toint(length)
@@ -108,6 +112,7 @@ class HexLib(s_stormtypes.Lib):
108
112
  except OverflowError as e:
109
113
  raise s_exc.BadArg(mesg=f'$lib.hex.fromint(): {e}')
110
114
 
115
+ @s_stormtypes.stormfunc(readonly=True)
111
116
  async def trimext(self, valu):
112
117
  valu = await s_stormtypes.tostr(valu)
113
118
 
@@ -124,6 +129,7 @@ class HexLib(s_stormtypes.Lib):
124
129
  break
125
130
  return valu
126
131
 
132
+ @s_stormtypes.stormfunc(readonly=True)
127
133
  async def signext(self, valu, length):
128
134
  valu = await s_stormtypes.tostr(valu)
129
135
  length = await s_stormtypes.toint(length)
@@ -494,6 +494,7 @@ class MitreAttackFlowLib(s_stormtypes.Lib):
494
494
  'norm': self._norm,
495
495
  }
496
496
 
497
+ @s_stormtypes.stormfunc(readonly=True)
497
498
  async def _norm(self, flow):
498
499
  flow = await s_stormtypes.toprim(flow)
499
500
 
@@ -594,7 +595,7 @@ class CvssLib(s_stormtypes.Lib):
594
595
  environmental score, overall score, and normalized vector string.
595
596
  The normalized vector string will have metrics ordered in
596
597
  specification order and metrics with undefined values will be
597
- removed. Example:
598
+ removed. Example::
598
599
 
599
600
  {
600
601
  'version': '3.1',
@@ -604,6 +605,7 @@ class CvssLib(s_stormtypes.Lib):
604
605
  'environmental': 4.3,
605
606
  'normalized': 'AV:N/AC:H/PR:L/UI:R/S:U/C:L/I:L/A:L'
606
607
  }
608
+
607
609
  '''}
608
610
  }},
609
611
  )
@@ -618,6 +620,7 @@ class CvssLib(s_stormtypes.Lib):
618
620
  'calculateFromProps': self.calculateFromProps,
619
621
  }
620
622
 
623
+ @s_stormtypes.stormfunc(readonly=True)
621
624
  async def vectToProps(self, text):
622
625
  s_common.deprecated('$lib.infosec.cvss.vectToProps()', '2.137.0', '3.0.0')
623
626
  await self.runt.snap.warnonce('$lib.infosec.cvss.vectToProps() is deprecated.')
@@ -689,6 +692,7 @@ class CvssLib(s_stormtypes.Lib):
689
692
 
690
693
  return rval
691
694
 
695
+ @s_stormtypes.stormfunc(readonly=True)
692
696
  async def calculateFromProps(self, props, vers='3.1'):
693
697
 
694
698
  vers = await s_stormtypes.tostr(vers)
@@ -814,6 +818,7 @@ class CvssLib(s_stormtypes.Lib):
814
818
 
815
819
  return rval
816
820
 
821
+ @s_stormtypes.stormfunc(readonly=True)
817
822
  async def vectToScore(self, vect, vers=None):
818
823
  vers = await s_stormtypes.tostr(vers, noneok=True)
819
824
 
@@ -39,6 +39,7 @@ class LibIpv6(s_stormtypes.Lib):
39
39
  'expand': self._expand,
40
40
  }
41
41
 
42
+ @s_stormtypes.stormfunc(readonly=True)
42
43
  async def _expand(self, valu):
43
44
  valu = await s_stormtypes.tostr(valu)
44
45
  valu = valu.strip()
@@ -1,3 +1,9 @@
1
+ import sys
2
+ import asyncio
3
+ import contextlib
4
+
5
+ import synapse.exc as s_exc
6
+
1
7
  import synapse.lib.stormtypes as s_stormtypes
2
8
 
3
9
  @s_stormtypes.registry.registerLib
@@ -10,7 +16,7 @@ class LibIters(s_stormtypes.Lib):
10
16
  _storm_locals = (
11
17
  {
12
18
  'name': 'enum', 'desc': 'Yield (<indx>, <item>) tuples from an iterable or generator.',
13
- 'type': {
19
+ 'type': {
14
20
  'type': 'function', '_funcname': 'enum',
15
21
  'args': (
16
22
  {'type': 'iter', 'name': 'genr', 'desc': 'An iterable or generator.'},
@@ -19,6 +25,17 @@ class LibIters(s_stormtypes.Lib):
19
25
  'desc': 'Yields (<indx>, <item>) tuples.'},
20
26
  }
21
27
  },
28
+ {
29
+ 'name': 'zip', 'desc': 'Yield tuples created by iterating multiple iterables in parallel.',
30
+ 'type': {
31
+ 'type': 'function', '_funcname': '_zip',
32
+ 'args': (
33
+ {'name': '*args', 'type': 'iter', 'desc': 'Iterables or generators.', },
34
+ ),
35
+ 'returns': {'name': 'yields', 'type': 'list',
36
+ 'desc': 'Yields tuples with an item from each iterable or generator.'},
37
+ }
38
+ },
22
39
  )
23
40
 
24
41
  def __init__(self, runt, name=()):
@@ -27,10 +44,50 @@ class LibIters(s_stormtypes.Lib):
27
44
  def getObjLocals(self):
28
45
  return {
29
46
  'enum': self.enum,
47
+ 'zip': self._zip,
30
48
  }
31
49
 
50
+ @s_stormtypes.stormfunc(readonly=True)
32
51
  async def enum(self, genr):
33
52
  indx = 0
34
53
  async for item in s_stormtypes.toiter(genr):
35
54
  yield (indx, item)
36
55
  indx += 1
56
+
57
+ @s_stormtypes.stormfunc(readonly=True)
58
+ async def _zip(self, *args):
59
+
60
+ async with contextlib.AsyncExitStack() as stack:
61
+ genrs = []
62
+ for arg in args:
63
+ agen = contextlib.aclosing(s_stormtypes.toiter(arg))
64
+ genrs.append(await stack.enter_async_context(agen))
65
+
66
+ try:
67
+ try:
68
+ while True:
69
+ tasks = []
70
+ async with asyncio.TaskGroup() as tg:
71
+ for genr in genrs:
72
+ tasks.append(tg.create_task(genr.__anext__()))
73
+
74
+ yield [task.result() for task in tasks]
75
+
76
+ await asyncio.sleep(0)
77
+ tasks.clear()
78
+
79
+ except* StopAsyncIteration:
80
+ pass
81
+
82
+ except ExceptionGroup as eg:
83
+ msgs = []
84
+ for exc in eg.exceptions:
85
+ if isinstance(exc, s_exc.SynErr):
86
+ msgs.append(f'({exc.errname}: {exc.errinfo.get("mesg")})')
87
+ else:
88
+ msgs.append(f'({exc.__class__.__name__}: {str(exc)})')
89
+
90
+ errs = len(msgs)
91
+ errm = ', '.join(msgs)
92
+ mesg = f'$lib.iters.zip() encountered errors in {errs} iterators during iteration: {errm}'
93
+ raise s_exc.StormRuntimeError(mesg=mesg)
@@ -62,9 +62,11 @@ class JsonSchema(s_stormtypes.StormType):
62
62
  'validate': self._validate,
63
63
  }
64
64
 
65
+ @s_stormtypes.stormfunc(readonly=True)
65
66
  async def _schema(self):
66
67
  return copy.deepcopy(self.schema)
67
68
 
69
+ @s_stormtypes.stormfunc(readonly=True)
68
70
  async def _validate(self, item):
69
71
  item = await s_stormtypes.toprim(item)
70
72
 
@@ -112,6 +114,7 @@ class JsonLib(s_stormtypes.Lib):
112
114
  'schema': self._jsonSchema,
113
115
  }
114
116
 
117
+ @s_stormtypes.stormfunc(readonly=True)
115
118
  async def _jsonSave(self, item):
116
119
  try:
117
120
  item = await s_stormtypes.toprim(item)
@@ -120,6 +123,7 @@ class JsonLib(s_stormtypes.Lib):
120
123
  mesg = f'Argument is not JSON compatible: {item}'
121
124
  raise s_exc.MustBeJsonSafe(mesg=mesg)
122
125
 
126
+ @s_stormtypes.stormfunc(readonly=True)
123
127
  async def _jsonLoad(self, text):
124
128
  text = await s_stormtypes.tostr(text)
125
129
  try:
@@ -128,6 +132,7 @@ class JsonLib(s_stormtypes.Lib):
128
132
  mesg = f'Text is not valid JSON: {text}'
129
133
  raise s_exc.BadJsonText(mesg=mesg)
130
134
 
135
+ @s_stormtypes.stormfunc(readonly=True)
131
136
  async def _jsonSchema(self, schema, use_default=True):
132
137
  schema = await s_stormtypes.toprim(schema)
133
138
  use_default = await s_stormtypes.tobool(use_default)
@@ -29,6 +29,7 @@ class LibMimeHtml(s_stormtypes.Lib):
29
29
  'totext': self.totext,
30
30
  }
31
31
 
32
+ @s_stormtypes.stormfunc(readonly=True)
32
33
  async def totext(self, html):
33
34
  html = await s_stormtypes.tostr(html)
34
35
  return await s_coro.semafork(htmlToText, html)
@@ -251,10 +251,12 @@ class LibModelTags(s_stormtypes.Lib):
251
251
  self.runt.confirm(('model', 'tag', 'set'))
252
252
  return await self.runt.snap.core.delTagModel(tagname)
253
253
 
254
+ @s_stormtypes.stormfunc(readonly=True)
254
255
  async def _getTagModel(self, tagname):
255
256
  tagname = await s_stormtypes.tostr(tagname)
256
257
  return await self.runt.snap.core.getTagModel(tagname)
257
258
 
259
+ @s_stormtypes.stormfunc(readonly=True)
258
260
  async def _listTagModel(self):
259
261
  return await self.runt.snap.core.listTagModel()
260
262
 
@@ -325,24 +327,28 @@ class LibModel(s_stormtypes.Lib):
325
327
  }
326
328
 
327
329
  @s_cache.memoizemethod(size=100)
330
+ @s_stormtypes.stormfunc(readonly=True)
328
331
  async def _methType(self, name):
329
332
  type_ = self.model.type(name)
330
333
  if type_ is not None:
331
334
  return ModelType(type_)
332
335
 
333
336
  @s_cache.memoizemethod(size=100)
337
+ @s_stormtypes.stormfunc(readonly=True)
334
338
  async def _methProp(self, name):
335
339
  prop = self.model.prop(name)
336
340
  if prop is not None:
337
341
  return ModelProp(prop)
338
342
 
339
343
  @s_cache.memoizemethod(size=100)
344
+ @s_stormtypes.stormfunc(readonly=True)
340
345
  async def _methForm(self, name):
341
346
  form = self.model.form(name)
342
347
  if form is not None:
343
348
  return ModelForm(form)
344
349
 
345
350
  @s_cache.memoize(size=100)
351
+ @s_stormtypes.stormfunc(readonly=True)
346
352
  async def _methTagProp(self, name):
347
353
  tagprop = self.model.getTagProp(name)
348
354
  if tagprop is not None:
@@ -387,6 +393,7 @@ class ModelForm(s_stormtypes.Prim):
387
393
  def _ctorFormType(self, path=None):
388
394
  return ModelType(self.valu.type, path=path)
389
395
 
396
+ @s_stormtypes.stormfunc(readonly=True)
390
397
  def _getFormProp(self, name):
391
398
  prop = self.valu.prop(name)
392
399
  if prop is not None:
@@ -490,15 +497,21 @@ class ModelType(s_stormtypes.Prim):
490
497
  s_stormtypes.Prim.__init__(self, valu, path=path)
491
498
  self.locls.update(self.getObjLocals())
492
499
  self.locls.update({'name': valu.name,
493
- 'norm': self._methNorm,
494
- 'repr': self._methRepr,
495
500
  'stortype': valu.stortype,
496
501
  })
497
502
 
503
+ def getObjLocals(self):
504
+ return {
505
+ 'norm': self._methNorm,
506
+ 'repr': self._methRepr,
507
+ }
508
+
509
+ @s_stormtypes.stormfunc(readonly=True)
498
510
  async def _methRepr(self, valu):
499
511
  nval = self.valu.norm(valu)
500
512
  return self.valu.repr(nval[0])
501
513
 
514
+ @s_stormtypes.stormfunc(readonly=True)
502
515
  async def _methNorm(self, valu):
503
516
  return self.valu.norm(valu)
504
517
 
@@ -544,7 +557,7 @@ class LibModelEdge(s_stormtypes.Lib):
544
557
  # Note: The use of extprops in hive paths in this class is an artifact of the
545
558
  # original implementation which used extended property language which had a
546
559
  # very bad cognitive overload with the cortex extended properties, but we
547
- # dont' want to change underlying data. epiphyte 20200703
560
+ # don't want to change underlying data. epiphyte 20200703
548
561
 
549
562
  # restrict list of keys which we allow to be set/del through this API.
550
563
  validedgekeys = (
@@ -578,9 +591,11 @@ class LibModelEdge(s_stormtypes.Lib):
578
591
  raise s_exc.NoSuchProp(mesg=f'The requested key is not valid for light edge metadata.',
579
592
  name=key)
580
593
 
594
+ @s_stormtypes.stormfunc(readonly=True)
581
595
  def _methValidKeys(self):
582
596
  return self.validedgekeys
583
597
 
598
+ @s_stormtypes.stormfunc(readonly=True)
584
599
  async def _methEdgeGet(self, verb):
585
600
  verb = await s_stormtypes.tostr(verb)
586
601
  await self._chkEdgeVerbInView(verb)
@@ -620,6 +635,7 @@ class LibModelEdge(s_stormtypes.Lib):
620
635
 
621
636
  await self.runt.snap.core.setHiveKey(path, kvdict)
622
637
 
638
+ @s_stormtypes.stormfunc(readonly=True)
623
639
  async def _methEdgeList(self):
624
640
  retn = []
625
641
  async for verb in self.runt.snap.view.getEdgeVerbs():
@@ -156,6 +156,7 @@ class LibModelExt(s_stormtypes.Lib):
156
156
  s_stormtypes.confirm(('model', 'tagprop', 'del'))
157
157
  await self.runt.snap.core.delTagProp(propname)
158
158
 
159
+ @s_stormtypes.stormfunc(readonly=True)
159
160
  async def getExtModel(self):
160
161
  return await self.runt.snap.core.getExtModel()
161
162
 
@@ -65,6 +65,7 @@ class NotifyLib(s_stormtypes.Lib):
65
65
  # 'bytype':
66
66
  }
67
67
 
68
+ @s_stormtypes.stormfunc(readonly=True)
68
69
  async def get(self, indx):
69
70
  indx = await s_stormtypes.toint(indx)
70
71
  mesg = await self.runt.snap.core.getUserNotif(indx)
@@ -81,6 +82,7 @@ class NotifyLib(s_stormtypes.Lib):
81
82
  raise s_exc.AuthDeny(mesg=mesg, user=self.runt.user.iden, username=self.runt.user.name)
82
83
  await self.runt.snap.core.delUserNotif(indx)
83
84
 
85
+ @s_stormtypes.stormfunc(readonly=True)
84
86
  async def list(self, size=None):
85
87
  size = await s_stormtypes.toint(size, noneok=True)
86
88
  async for mesg in self.runt.snap.core.iterUserNotifs(self.runt.user.iden, size=size):
@@ -34,6 +34,7 @@ class LibPack(s_stormtypes.Lib):
34
34
  'un': self.un,
35
35
  }
36
36
 
37
+ @s_stormtypes.stormfunc(readonly=True)
37
38
  async def en(self, fmt, items):
38
39
  fmt = await s_stormtypes.tostr(fmt)
39
40
  items = await s_stormtypes.toprim(items)
@@ -47,6 +48,7 @@ class LibPack(s_stormtypes.Lib):
47
48
  except struct.error as e:
48
49
  raise s_exc.BadArg(mesg=str(e))
49
50
 
51
+ @s_stormtypes.stormfunc(readonly=True)
50
52
  async def un(self, fmt, byts, offs=0):
51
53
 
52
54
  fmt = await s_stormtypes.tostr(fmt)
@@ -26,6 +26,7 @@ class LibRandom(s_stormtypes.Lib):
26
26
  'int': self._int,
27
27
  }
28
28
 
29
+ @s_stormtypes.stormfunc(readonly=True)
29
30
  async def _int(self, maxval, minval=0):
30
31
  maxval = await s_stormtypes.toint(maxval)
31
32
  minval = await s_stormtypes.toint(minval)
@@ -130,19 +130,15 @@ class SmtpMessage(s_stormtypes.StormType):
130
130
  'sender': self._setSenderEmail,
131
131
  })
132
132
 
133
- @s_stormtypes.stormfunc(readonly=True)
134
133
  async def _setSenderEmail(self, valu):
135
- # TODO handle inet:email and ps:contact Nodes
136
134
  self.sender = await s_stormtypes.tostr(valu)
137
135
 
138
136
  async def _getSenderEmail(self):
139
137
  return self.sender
140
138
 
141
- @s_stormtypes.stormfunc(readonly=True)
142
139
  async def _setEmailText(self, text):
143
140
  self.bodytext = await s_stormtypes.tostr(text)
144
141
 
145
- @s_stormtypes.stormfunc(readonly=True)
146
142
  async def _setEmailHtml(self, html):
147
143
  self.bodyhtml = await s_stormtypes.tostr(html)
148
144
 
@@ -152,9 +148,6 @@ class SmtpMessage(s_stormtypes.StormType):
152
148
  async def _getEmailHtml(self):
153
149
  return self.bodyhtml
154
150
 
155
- # TODO
156
- # async def attach(self, sha256, name, mime):
157
-
158
151
  async def send(self, host, port=25, user=None, passwd=None, usetls=False, starttls=False, timeout=60):
159
152
 
160
153
  self.runt.confirm(('storm', 'inet', 'smtp', 'send'))