synapse 2.195.0__py311-none-any.whl → 2.196.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 (46) hide show
  1. synapse/axon.py +72 -5
  2. synapse/common.py +23 -0
  3. synapse/cortex.py +1 -0
  4. synapse/daemon.py +1 -0
  5. synapse/lib/aha.py +159 -4
  6. synapse/lib/cell.py +133 -8
  7. synapse/lib/jsonstor.py +2 -1
  8. synapse/lib/modelrev.py +5 -1
  9. synapse/lib/nexus.py +13 -6
  10. synapse/lib/reflect.py +4 -5
  11. synapse/lib/snap.py +14 -7
  12. synapse/lib/stormlib/aha.py +351 -1
  13. synapse/lib/stormlib/utils.py +37 -0
  14. synapse/lib/stormtypes.py +124 -6
  15. synapse/lib/version.py +2 -2
  16. synapse/models/base.py +3 -0
  17. synapse/models/infotech.py +55 -16
  18. synapse/models/orgs.py +14 -10
  19. synapse/models/risk.py +23 -10
  20. synapse/models/transport.py +8 -3
  21. synapse/telepath.py +12 -0
  22. synapse/tests/test_axon.py +23 -0
  23. synapse/tests/test_common.py +28 -0
  24. synapse/tests/test_datamodel.py +8 -0
  25. synapse/tests/test_lib_aha.py +241 -0
  26. synapse/tests/test_lib_cell.py +61 -0
  27. synapse/tests/test_lib_jsonstor.py +1 -0
  28. synapse/tests/test_lib_modelrev.py +6 -0
  29. synapse/tests/test_lib_nexus.py +24 -2
  30. synapse/tests/test_lib_stormlib_aha.py +188 -0
  31. synapse/tests/test_lib_stormlib_utils.py +14 -0
  32. synapse/tests/test_lib_stormtypes.py +90 -3
  33. synapse/tests/test_model_base.py +2 -0
  34. synapse/tests/test_model_infotech.py +28 -1
  35. synapse/tests/test_model_orgs.py +2 -0
  36. synapse/tests/test_model_risk.py +2 -0
  37. synapse/tests/test_model_transport.py +1 -0
  38. synapse/tests/test_telepath.py +26 -0
  39. synapse/tests/test_tools_aha.py +192 -0
  40. synapse/tools/aha/mirror.py +193 -0
  41. synapse/tools/changelog.py +32 -27
  42. {synapse-2.195.0.dist-info → synapse-2.196.0.dist-info}/METADATA +1 -1
  43. {synapse-2.195.0.dist-info → synapse-2.196.0.dist-info}/RECORD +46 -43
  44. {synapse-2.195.0.dist-info → synapse-2.196.0.dist-info}/LICENSE +0 -0
  45. {synapse-2.195.0.dist-info → synapse-2.196.0.dist-info}/WHEEL +0 -0
  46. {synapse-2.195.0.dist-info → synapse-2.196.0.dist-info}/top_level.txt +0 -0
synapse/lib/snap.py CHANGED
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ import sys
3
4
  import types
4
5
  import asyncio
5
6
  import logging
@@ -318,7 +319,7 @@ class ProtoNode:
318
319
 
319
320
  if prop.locked:
320
321
  mesg = f'Tagprop {name} is locked.'
321
- return await self.ctx.snap._raiseOnStrict(s_exc.IsDeprLocked, mesg)
322
+ return await self.ctx.snap._raiseOnStrict(s_exc.IsDeprLocked, mesg, prop=name)
322
323
 
323
324
  try:
324
325
  norm, info = prop.type.norm(valu)
@@ -348,14 +349,14 @@ class ProtoNode:
348
349
 
349
350
  if prop.locked:
350
351
  mesg = f'Prop {prop.full} is locked due to deprecation.'
351
- await self.ctx.snap._raiseOnStrict(s_exc.IsDeprLocked, mesg)
352
+ await self.ctx.snap._raiseOnStrict(s_exc.IsDeprLocked, mesg, prop=prop.full)
352
353
  return False
353
354
 
354
355
  if isinstance(prop.type, s_types.Array):
355
356
  arrayform = self.ctx.snap.core.model.form(prop.type.arraytype.name)
356
357
  if arrayform is not None and arrayform.locked:
357
358
  mesg = f'Prop {prop.full} is locked due to deprecation.'
358
- await self.ctx.snap._raiseOnStrict(s_exc.IsDeprLocked, mesg)
359
+ await self.ctx.snap._raiseOnStrict(s_exc.IsDeprLocked, mesg, prop=prop.full)
359
360
  return False
360
361
 
361
362
  if norminfo is None:
@@ -375,7 +376,7 @@ class ProtoNode:
375
376
  ndefform = self.ctx.snap.core.model.form(valu[0])
376
377
  if ndefform.locked:
377
378
  mesg = f'Prop {prop.full} is locked due to deprecation.'
378
- await self.ctx.snap._raiseOnStrict(s_exc.IsDeprLocked, mesg)
379
+ await self.ctx.snap._raiseOnStrict(s_exc.IsDeprLocked, mesg, prop=prop.full)
379
380
  return False
380
381
 
381
382
  curv = self.get(prop.name)
@@ -414,7 +415,8 @@ class ProtoNode:
414
415
  if propsubs is not None:
415
416
  for subname, subvalu in propsubs.items():
416
417
  full = f'{prop.name}:{subname}'
417
- if self.form.props.get(full) is not None:
418
+ subprop = self.form.props.get(full)
419
+ if subprop is not None and not subprop.locked:
418
420
  await self.set(full, subvalu)
419
421
 
420
422
  propadds = norminfo.get('adds')
@@ -445,7 +447,8 @@ class ProtoNode:
445
447
  if propsubs is not None:
446
448
  for subname, subvalu in propsubs.items():
447
449
  full = f'{prop.name}:{subname}'
448
- if self.form.props.get(full) is not None:
450
+ subprop = self.form.props.get(full)
451
+ if subprop is not None and not subprop.locked:
449
452
  ops.append(self.getSetOps(full, subvalu))
450
453
 
451
454
  propadds = norminfo.get('adds')
@@ -487,7 +490,7 @@ class SnapEditor:
487
490
 
488
491
  if form.locked:
489
492
  mesg = f'Form {form.full} is locked due to deprecation for valu={valu}.'
490
- return await self.snap._raiseOnStrict(s_exc.IsDeprLocked, mesg)
493
+ return await self.snap._raiseOnStrict(s_exc.IsDeprLocked, mesg, prop=form.full)
491
494
 
492
495
  if norminfo is None:
493
496
  try:
@@ -1615,6 +1618,10 @@ class Snap(s_base.Base):
1615
1618
  return tagnode
1616
1619
 
1617
1620
  async def _raiseOnStrict(self, ctor, mesg, **info):
1621
+ if __debug__:
1622
+ if issubclass(ctor, s_exc.IsDeprLocked):
1623
+ sys.audit('synapse.exc.IsDeprLocked', (mesg, info))
1624
+
1618
1625
  if self.strict:
1619
1626
  raise ctor(mesg=mesg, **info)
1620
1627
  await self.warn(mesg)
@@ -1,4 +1,7 @@
1
+ import textwrap
2
+
1
3
  import synapse.exc as s_exc
4
+ import synapse.common as s_common
2
5
  import synapse.lib.stormtypes as s_stormtypes
3
6
 
4
7
  @s_stormtypes.registry.registerLib
@@ -49,6 +52,80 @@ class AhaLib(s_stormtypes.Lib):
49
52
  'type': {'type': 'function', '_funcname': '_methAhaList', 'args': (),
50
53
  'returns': {'name': 'Yields', 'type': 'list',
51
54
  'desc': 'The AHA service dictionaries.', }}},
55
+ {'name': 'callPeerApi', 'desc': '''Call an API on all peers (leader and mirrors) of an AHA service and yield the responses from each.
56
+
57
+ Examples:
58
+ Call getCellInfo on an AHA service::
59
+
60
+ $todo = $lib.utils.todo('getCellInfo')
61
+ for $info in $lib.aha.callPeerApi(cortex..., $todo) {
62
+ $lib.print($info)
63
+ }
64
+
65
+ Call getCellInfo on an AHA service, skipping the invoking service::
66
+
67
+ $todo = $lib.utils.todo('getCellInfo')
68
+ for $info in $lib.aha.callPeerApi(cortex..., $todo, skiprun=$lib.cell.getCellInfo().cell.run) {
69
+ $lib.print($info)
70
+ }
71
+
72
+ Call method with arguments::
73
+
74
+ $todo = $lib.utils.todo(('method', ([1, 2]), ({'foo': 'bar'})))
75
+ for $info in $lib.aha.callPeerApi(cortex..., $todo) {
76
+ $lib.print($info)
77
+ }
78
+
79
+ ''',
80
+ 'type': {'type': 'function', '_funcname': '_methCallPeerApi',
81
+ 'args': (
82
+ {'name': 'svcname', 'type': 'str',
83
+ 'desc': 'The name of the AHA service to call. It is easiest to use the relative name of a service, ending with "...".', },
84
+ {'name': 'todo', 'type': 'list',
85
+ 'desc': 'The todo tuple (name, args, kwargs).'},
86
+ {'name': 'timeout', 'type': 'int', 'default': None,
87
+ 'desc': 'Optional timeout in seconds.'},
88
+ {'name': 'skiprun', 'type': 'str', 'default': None,
89
+ 'desc': '''Optional run ID argument that allows skipping results from a specific service run ID.
90
+ This is most often used to omit the invoking service from the results, ensuring that only responses from other services are included.
91
+ '''},
92
+ ),
93
+ 'returns': {'name': 'yields', 'type': 'list',
94
+ 'desc': 'Yields the results of the API calls as tuples of (svcname, (ok, info)).', }}},
95
+ {'name': 'callPeerGenr', 'desc': '''Call a generator API on all peers (leader and mirrors) of an AHA service and yield the responses from each.
96
+
97
+ Examples:
98
+ Call getNexusChanges on an AHA service::
99
+
100
+ $todo = $lib.utils.todo('getNexusChanges', (0), wait=$lib.false)
101
+ for $info in $lib.aha.callPeerGenr(cortex..., $todo) {
102
+ $lib.print($info)
103
+ }
104
+
105
+ Call getNexusChanges on an AHA service, skipping the invoking service::
106
+
107
+ $todo = $lib.utils.todo('getNexusChanges', (0), wait=$lib.false)
108
+ for $info in $lib.aha.callPeerGenr(cortex..., $todo, skiprun=$lib.cell.getCellInfo().cell.run) {
109
+ $lib.print($info)
110
+ }
111
+
112
+ ''',
113
+ 'type': {'type': 'function', '_funcname': '_methCallPeerGenr',
114
+ 'args': (
115
+ {'name': 'svcname', 'type': 'str',
116
+ 'desc': 'The name of the AHA service to call. It is easiest to use the relative name of a service, ending with "...".', },
117
+ {'name': 'todo', 'type': 'list',
118
+ 'desc': 'The todo tuple (name, args, kwargs).'},
119
+ {'name': 'timeout', 'type': 'int', 'default': None,
120
+ 'desc': 'Optional timeout in seconds.'},
121
+ {'name': 'skiprun', 'type': 'str', 'default': None,
122
+ 'desc': '''Optional run ID argument that allows skipping results from a specific service run ID.
123
+ This is most often used to omit the invoking service from the results, ensuring that only responses from other services are included.
124
+ '''},
125
+ ),
126
+ 'returns': {'name': 'yields', 'type': 'list',
127
+ 'desc': 'Yields the results of the API calls as tuples containing (svcname, (ok, info)).', }}}
128
+
52
129
  )
53
130
  _storm_lib_path = ('aha',)
54
131
  def getObjLocals(self):
@@ -56,6 +133,8 @@ class AhaLib(s_stormtypes.Lib):
56
133
  'del': self._methAhaDel,
57
134
  'get': self._methAhaGet,
58
135
  'list': self._methAhaList,
136
+ 'callPeerApi': self._methCallPeerApi,
137
+ 'callPeerGenr': self._methCallPeerGenr,
59
138
  }
60
139
 
61
140
  @s_stormtypes.stormfunc(readonly=True)
@@ -85,6 +164,62 @@ class AhaLib(s_stormtypes.Lib):
85
164
  proxy = await self.runt.snap.core.reqAhaProxy()
86
165
  return await proxy.getAhaSvc(svcname, filters=filters)
87
166
 
167
+ async def _methCallPeerApi(self, svcname, todo, timeout=None, skiprun=None):
168
+ '''
169
+ Call an API on an AHA service.
170
+
171
+ Args:
172
+ svcname (str): The name of the AHA service to call.
173
+ todo (list): The todo tuple from $lib.utils.todo().
174
+ timeout (int): Optional timeout in seconds.
175
+ skiprun (str): Optional run ID argument allows skipping self-enumeration.
176
+ '''
177
+ svcname = await s_stormtypes.tostr(svcname)
178
+ todo = await s_stormtypes.toprim(todo)
179
+ timeout = await s_stormtypes.toint(timeout, noneok=True)
180
+ skiprun = await s_stormtypes.tostr(skiprun, noneok=True)
181
+
182
+ proxy = await self.runt.snap.core.reqAhaProxy()
183
+ svc = await proxy.getAhaSvc(svcname)
184
+ if svc is None:
185
+ raise s_exc.NoSuchName(mesg=f'No AHA service found for {svcname}')
186
+
187
+ svcinfo = svc.get('svcinfo')
188
+ svciden = svcinfo.get('iden')
189
+ if svciden is None:
190
+ raise s_exc.NoSuchName(mesg=f'Service {svcname} has no iden')
191
+
192
+ async for svcname, (ok, info) in proxy.callAhaPeerApi(svciden, todo, timeout=timeout, skiprun=skiprun):
193
+ yield (svcname, (ok, info))
194
+
195
+ async def _methCallPeerGenr(self, svcname, todo, timeout=None, skiprun=None):
196
+ '''
197
+ Call a generator API on an AHA service.
198
+
199
+ Args:
200
+ svcname (str): The name of the AHA service to call.
201
+ todo (list): The todo tuple from $lib.utils.todo().
202
+ timeout (int): Optional timeout in seconds.
203
+ skiprun (str): Optional run ID argument allows skipping self-enumeration.
204
+ '''
205
+ svcname = await s_stormtypes.tostr(svcname)
206
+ todo = await s_stormtypes.toprim(todo)
207
+ timeout = await s_stormtypes.toint(timeout, noneok=True)
208
+ skiprun = await s_stormtypes.tostr(skiprun, noneok=True)
209
+
210
+ proxy = await self.runt.snap.core.reqAhaProxy()
211
+ svc = await proxy.getAhaSvc(svcname)
212
+ if svc is None:
213
+ raise s_exc.NoSuchName(mesg=f'No AHA service found for {svcname}')
214
+
215
+ svcinfo = svc.get('svcinfo')
216
+ svciden = svcinfo.get('iden')
217
+ if svciden is None:
218
+ raise s_exc.NoSuchName(mesg=f'Service {svcname} has no iden')
219
+
220
+ async for svcname, (ok, info) in proxy.callAhaPeerGenr(svciden, todo, timeout=timeout, skiprun=skiprun):
221
+ yield (svcname, (ok, info))
222
+
88
223
  @s_stormtypes.registry.registerLib
89
224
  class AhaPoolLib(s_stormtypes.Lib):
90
225
  '''
@@ -531,5 +666,220 @@ The ready column indicates that a service has entered into the realtime change w
531
666
  }
532
667
  }
533
668
  '''
534
- }
669
+ },
670
+ {
671
+ 'name': 'aha.svc.mirror',
672
+ 'descr': textwrap.dedent('''\
673
+ Query the AHA services and their mirror relationships.
674
+
675
+ Note: non-mirror services are not displayed.
676
+ '''),
677
+ 'cmdargs': (
678
+ ('--timeout', {'help': 'The timeout in seconds for individual service API calls.',
679
+ 'default': 10, 'type': 'int'}),
680
+ ('--wait', {'help': 'Whether to wait for the mirrors to sync.',
681
+ 'action': 'store_true'}),
682
+ ),
683
+ 'storm': '''
684
+ init {
685
+ $conf = ({
686
+ "columns": [
687
+ {"name": "name", "width": 40},
688
+ {"name": "role", "width": 9},
689
+ {"name": "online", "width": 7},
690
+ {"name": "ready", "width": 6},
691
+ {"name": "host", "width": 16},
692
+ {"name": "port", "width": 8},
693
+ {"name": "version", "width": 12},
694
+ {"name": "nexus idx", "width": 10},
695
+ ],
696
+ "separators": {
697
+ "row:outline": false,
698
+ "column:outline": false,
699
+ "header:row": "#",
700
+ "data:row": "",
701
+ "column": "",
702
+ },
703
+ })
704
+ $printer = $lib.tabular.printer($conf)
705
+ $timeout = $cmdopts.timeout
706
+ $wait = $cmdopts.wait
707
+ }
708
+
709
+ function get_cell_infos(vname, timeout) {
710
+ $cell_infos = ({})
711
+ $todo = $lib.utils.todo('getCellInfo')
712
+ for $info in $lib.aha.callPeerApi($vname, $todo, timeout=$timeout) {
713
+ $svcname = $info.0
714
+ ($ok, $info) = $info.1
715
+ if $ok {
716
+ $cell_infos.$svcname = $info
717
+ }
718
+ }
719
+ return($cell_infos)
720
+ }
721
+
722
+ function build_status_list(members, cell_infos) {
723
+ $group_status = ()
724
+ for $svc in $members {
725
+ $svcinfo = $svc.svcinfo
726
+ $svcname = $svc.name
727
+ $status = ({
728
+ 'name': $svcname,
729
+ 'role': '<unknown>',
730
+ 'online': $lib.dict.has($svcinfo, 'online'),
731
+ 'ready': $svcinfo.ready,
732
+ 'host': $svcinfo.urlinfo.host,
733
+ 'port': $svcinfo.urlinfo.port,
734
+ 'version': '<unknown>',
735
+ 'nexs_indx': (0)
736
+ })
737
+ if ($cell_infos.$svcname) {
738
+ $info = $cell_infos.$svcname
739
+ $cell_info = $info.cell
740
+ $status.nexs_indx = $cell_info.nexsindx
741
+ if ($cell_info.uplink) {
742
+ $status.role = 'follower'
743
+ } else {
744
+ $status.role = 'leader'
745
+ }
746
+ $status.version = $info.synapse.verstring
747
+ }
748
+ $group_status.append($status)
749
+ }
750
+ return($group_status)
751
+ }
752
+
753
+ function check_sync_status(group_status) {
754
+ $indices = $lib.set()
755
+ $known_count = (0)
756
+ for $status in $group_status {
757
+ $indices.add($status.nexs_indx)
758
+ $known_count = ($known_count + (1))
759
+ }
760
+ if ($lib.len($indices) = 1) {
761
+ if ($known_count = $lib.len($group_status)) {
762
+ return(true)
763
+ }
764
+ }
765
+ }
766
+
767
+ function output_status(vname, group_status, printer) {
768
+ $lib.print($printer.header())
769
+ $lib.print($vname)
770
+ for $status in $group_status {
771
+ if ($status.nexs_indx = 0) {
772
+ $status.nexs_indx = '<unknown>'
773
+ }
774
+ $row = (
775
+ $status.name,
776
+ $status.role,
777
+ $status.online,
778
+ $status.ready,
779
+ $status.host,
780
+ $status.port,
781
+ $status.version,
782
+ $status.nexs_indx
783
+ )
784
+ $lib.print($printer.row($row))
785
+ }
786
+ }
787
+
788
+ $virtual_services = ({})
789
+ $member_servers = ({})
790
+
791
+ for $svc in $lib.aha.list() {
792
+ $name = $svc.name
793
+ $svcinfo = $svc.svcinfo
794
+ $urlinfo = $svcinfo.urlinfo
795
+ $hostname = $urlinfo.hostname
796
+
797
+ if ($name != $hostname) {
798
+ $virtual_services.$name = $svc
799
+ } else {
800
+ $member_servers.$name = $svc
801
+ }
802
+ }
803
+
804
+ $mirror_groups = ({})
805
+ for ($vname, $vsvc) in $virtual_services {
806
+ $vsvc_info = $vsvc.svcinfo
807
+ $vsvc_iden = $vsvc_info.iden
808
+ $vsvc_leader = $vsvc_info.leader
809
+ $vsvc_hostname = $vsvc_info.urlinfo.hostname
810
+
811
+ if (not $vsvc_iden or not $vsvc_hostname or not $vsvc_leader) {
812
+ continue
813
+ }
814
+
815
+ $primary_member = $member_servers.$vsvc_hostname
816
+ if (not $primary_member) {
817
+ continue
818
+ }
819
+
820
+ $members = ([$primary_member])
821
+ for ($mname, $msvc) in $member_servers {
822
+ if ($mname != $vsvc_hostname) {
823
+ $msvc_info = $msvc.svcinfo
824
+ if ($msvc_info.iden = $vsvc_iden and $msvc_info.leader = $vsvc_leader) {
825
+ $members.append($msvc)
826
+ }
827
+ }
828
+ }
829
+
830
+ if ($lib.len($members) > 1) {
831
+ $mirror_groups.$vname = $members
832
+ }
833
+ }
834
+
835
+ for ($vname, $members) in $mirror_groups {
836
+ $cell_infos = $get_cell_infos($vname, $timeout)
837
+ $group_status = $build_status_list($members, $cell_infos)
838
+ $lib.print('Service Mirror Groups:')
839
+ $output_status($vname, $group_status, $printer)
840
+
841
+ if $check_sync_status($group_status) {
842
+ $lib.print('Group Status: In Sync')
843
+ } else {
844
+ $lib.print(`Group Status: Out of Sync`)
845
+ if $wait {
846
+ $leader_nexs = (0)
847
+ for $status in $group_status {
848
+ if (($status.role = 'leader') and ($status.nexs_indx > 0)) {
849
+ $leader_nexs = $status.nexs_indx
850
+ }
851
+ }
852
+ if ($leader_nexs > 0) {
853
+ while (true) {
854
+ $responses = ()
855
+ $todo = $lib.utils.todo(waitNexsOffs, ($leader_nexs - 1), timeout=$timeout)
856
+ for $info in $lib.aha.callPeerApi($vname, $todo, timeout=$timeout) {
857
+ $svcname = $info.0
858
+ ($ok, $info) = $info.1
859
+ if ($ok and $info) {
860
+ $responses.append(($svcname, $info))
861
+ }
862
+ }
863
+
864
+ if ($lib.len($responses) = $lib.len($members)) {
865
+ $cell_infos = $get_cell_infos($vname, $timeout)
866
+ $group_status = $build_status_list($members, $cell_infos)
867
+
868
+ $lib.print('')
869
+ $lib.print('Updated status:')
870
+ $output_status($vname, $group_status, $printer)
871
+
872
+ if $check_sync_status($group_status) {
873
+ $lib.print('Group Status: In Sync')
874
+ break
875
+ }
876
+ }
877
+ }
878
+ }
879
+ }
880
+ }
881
+ $lib.print('')
882
+ }
883
+ '''
884
+ },
535
885
  )
@@ -0,0 +1,37 @@
1
+ import synapse.lib.stormtypes as s_stormtypes
2
+
3
+ @s_stormtypes.registry.registerLib
4
+ class LibUtils(s_stormtypes.Lib):
5
+ '''
6
+ A Storm library for working with utility functions.
7
+ '''
8
+ _storm_locals = (
9
+ {'name': 'todo',
10
+ 'desc': '''
11
+ Create a todo tuple of (name, args, kwargs).
12
+ ''',
13
+ 'type': {'type': 'function', '_funcname': '_todo',
14
+ 'args': (
15
+ {'name': '_todoname', 'type': 'str',
16
+ 'desc': 'The todo name.'},
17
+ {'name': '*args', 'type': 'any',
18
+ 'desc': 'Positional arguments for the todo.'},
19
+ {'name': '**kwargs', 'type': 'any',
20
+ 'desc': 'Keyword arguments for the todo.'},
21
+ ),
22
+ 'returns': {'type': 'list', 'desc': 'A todo tuple of (name, args, kwargs).'},
23
+ }},
24
+ )
25
+ _storm_lib_path = ('utils',)
26
+
27
+ def getObjLocals(self):
28
+ return {
29
+ 'todo': self._todo,
30
+ }
31
+
32
+ @s_stormtypes.stormfunc(readonly=True)
33
+ async def _todo(self, _todoname, *args, **kwargs):
34
+ _todoname = await s_stormtypes.tostr(_todoname)
35
+ args = await s_stormtypes.toprim(args)
36
+ kwargs = await s_stormtypes.toprim(kwargs)
37
+ return (_todoname, args, kwargs)