synapse 2.198.0__py311-none-any.whl → 2.200.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.

synapse/cortex.py CHANGED
@@ -20,7 +20,6 @@ import synapse.lib.base as s_base
20
20
  import synapse.lib.cell as s_cell
21
21
  import synapse.lib.chop as s_chop
22
22
  import synapse.lib.coro as s_coro
23
- import synapse.lib.hive as s_hive
24
23
  import synapse.lib.view as s_view
25
24
  import synapse.lib.cache as s_cache
26
25
  import synapse.lib.const as s_const
@@ -855,7 +854,6 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
855
854
  cellapi = CoreApi
856
855
  viewapi = s_view.ViewApi
857
856
  layerapi = s_layer.LayerApi
858
- hiveapi = s_hive.HiveApi
859
857
 
860
858
  viewctor = s_view.View.anit
861
859
  layrctor = s_layer.Layer.anit
@@ -4656,10 +4654,6 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
4656
4654
  if not path:
4657
4655
  return await self.cellapi.anit(self, link, user)
4658
4656
 
4659
- if path[0] == 'hive' and user.isAdmin():
4660
- s_common.deprecated('Cortex /hive telepath path', curv='2.198.0', eolv='2.199.0')
4661
- return await self.hiveapi.anit(self.hive, user)
4662
-
4663
4657
  if path[0] == 'layer':
4664
4658
 
4665
4659
  if len(path) == 1:
synapse/datamodel.py CHANGED
@@ -1,6 +1,7 @@
1
1
  '''
2
2
  An API to assist with the creation and enforcement of cortex data models.
3
3
  '''
4
+ import sys
4
5
  import asyncio
5
6
  import logging
6
7
  import collections
@@ -15,6 +16,7 @@ import synapse.lib.cache as s_cache
15
16
  import synapse.lib.types as s_types
16
17
  import synapse.lib.dyndeps as s_dyndeps
17
18
  import synapse.lib.grammar as s_grammar
19
+ import synapse.lib.msgpack as s_msgpack
18
20
 
19
21
  logger = logging.getLogger(__name__)
20
22
 
@@ -142,6 +144,9 @@ class Prop:
142
144
  async def depfunc(node, oldv):
143
145
  mesg = f'The property {self.full} is deprecated or using a deprecated type and will be removed in 3.0.0'
144
146
  await node.snap.warnonce(mesg)
147
+ if __debug__:
148
+ sys.audit('synapse.datamodel.Prop.deprecated', mesg, self.full)
149
+
145
150
  self.onSet(depfunc)
146
151
 
147
152
  def __repr__(self):
@@ -306,6 +311,8 @@ class Form:
306
311
  async def depfunc(node):
307
312
  mesg = f'The form {self.full} is deprecated or using a deprecated type and will be removed in 3.0.0'
308
313
  await node.snap.warnonce(mesg)
314
+ if __debug__:
315
+ sys.audit('synapse.datamodel.Form.deprecated', mesg, self.full)
309
316
  self.onAdd(depfunc)
310
317
 
311
318
  def getStorNode(self, form):
@@ -1107,7 +1114,7 @@ class Model:
1107
1114
 
1108
1115
  def _prepFormIface(self, form, iface):
1109
1116
 
1110
- template = iface.get('template', {})
1117
+ template = s_msgpack.deepcopy(iface.get('template', {}))
1111
1118
  template.update(form.type.info.get('template', {}))
1112
1119
 
1113
1120
  def convert(item):
synapse/lib/hive.py CHANGED
@@ -86,7 +86,7 @@ class Node(s_base.Base):
86
86
  for name, node in self.kids.items():
87
87
  yield name, node
88
88
 
89
- class Hive(s_nexus.Pusher, s_telepath.Aware):
89
+ class Hive(s_nexus.Pusher):
90
90
  '''
91
91
  An optionally persistent atomically accessed tree which implements
92
92
  primitives for use in making distributed/clustered services.
@@ -95,8 +95,6 @@ class Hive(s_nexus.Pusher, s_telepath.Aware):
95
95
 
96
96
  await s_nexus.Pusher.__anit__(self, 'hive', nexsroot=nexsroot)
97
97
 
98
- s_telepath.Aware.__init__(self)
99
-
100
98
  if conf is None:
101
99
  conf = {}
102
100
 
@@ -161,24 +159,6 @@ class Hive(s_nexus.Pusher, s_telepath.Aware):
161
159
  for cullname in culls:
162
160
  await node.pop((cullname,))
163
161
 
164
- async def getHiveAuth(self):
165
- '''
166
- Retrieve a HiveAuth for hive standalone or non-cell uses.
167
-
168
- Note:
169
- This is for the hive's own auth, or for non-cell auth. It isn't the same auth as for a cell
170
- '''
171
- import synapse.lib.hiveauth as s_hiveauth
172
- if self.auth is None:
173
-
174
- path = tuple(self.conf.get('auth:path').split('/'))
175
-
176
- node = await self.open(path)
177
- self.auth = await s_hiveauth.Auth.anit(node, nexsroot=self.nexsroot)
178
- self.onfini(self.auth.fini)
179
-
180
- return self.auth
181
-
182
162
  async def _onHiveFini(self):
183
163
  await self.root.fini()
184
164
 
@@ -423,27 +403,6 @@ class Hive(s_nexus.Pusher, s_telepath.Aware):
423
403
 
424
404
  return node.valu
425
405
 
426
- async def getTeleApi(self, link, mesg, path):
427
- s_common.deprecated('Hive.getTeleApi', curv='2.198.0', eolv='2.199.0')
428
- auth = await self.getHiveAuth()
429
-
430
- if not self.conf.get('auth:en'):
431
- user = await auth.getUserByName('root')
432
- return await HiveApi.anit(self, user)
433
-
434
- name, info = mesg[1].get('auth')
435
-
436
- user = await auth.getUserByName(name)
437
- if user is None:
438
- raise s_exc.NoSuchUser(name=name)
439
-
440
- # passwd None always fails...
441
- passwd = info.get('passwd')
442
- if not await user.tryPasswd(passwd):
443
- raise s_exc.AuthDeny(mesg='Invalid password', user=user.iden, username=user.name)
444
-
445
- return await HiveApi.anit(self, user)
446
-
447
406
  async def _storLoadHive(self):
448
407
  pass
449
408
 
@@ -480,224 +439,6 @@ class SlabHive(Hive):
480
439
  lkey = '\x00'.join(full).encode('utf8')
481
440
  self.slab.pop(lkey, db=self.db)
482
441
 
483
- class HiveApi(s_base.Base):
484
-
485
- async def __anit__(self, hive, user):
486
-
487
- await s_base.Base.__anit__(self)
488
-
489
- self.hive = hive
490
- self.user = user
491
-
492
- self.msgq = asyncio.Queue(maxsize=10000)
493
-
494
- self.onfini(self._onHapiFini)
495
-
496
- async def loadHiveTree(self, tree, path=(), trim=False):
497
- s_common.deprecated('HiveApi.loadHiveTree', curv='2.167.0')
498
- return await self.hive.loadHiveTree(tree, path=path, trim=trim)
499
-
500
- async def saveHiveTree(self, path=()):
501
- s_common.deprecated('HiveApi.saveHiveTree', curv='2.167.0')
502
- return await self.hive.saveHiveTree(path=path)
503
-
504
- async def treeAndSync(self, path, iden):
505
- s_common.deprecated('HiveApi.treeAndSync', curv='2.167.0')
506
-
507
- node = await self.hive.open(path)
508
-
509
- # register handlers...
510
- node.on('hive:add', self._onHiveEdit, base=self)
511
- node.on('hive:set', self._onHiveEdit, base=self)
512
- node.on('hive:pop', self._onHiveEdit, base=self)
513
-
514
- # serialize the subtree into a message and return
515
- # via the mesg queue so there is no get/update race
516
- root = (node.valu, {})
517
-
518
- todo = collections.deque([(node, root)])
519
-
520
- # breadth first generator
521
- while todo:
522
-
523
- node, pode = todo.popleft()
524
-
525
- for name, kidn in node.kids.items():
526
-
527
- kidp = (kidn.valu, {})
528
- pode[1][name] = kidp
529
-
530
- todo.append((kidn, kidp))
531
-
532
- await self.msgq.put(('hive:tree', {'path': path, 'tree': root}))
533
- await self.msgq.put(('hive:sync', {'iden': iden}))
534
- return
535
-
536
- async def setAndSync(self, path, valu, iden, nexs=False):
537
- s_common.deprecated('HiveApi.setAndSync', curv='2.167.0')
538
-
539
- valu = await self.hive.set(path, valu, nexs=nexs)
540
- await self.msgq.put(('hive:sync', {'iden': iden}))
541
- return valu
542
-
543
- async def addAndSync(self, path, valu, iden):
544
- s_common.deprecated('HiveApi.addAndSync', curv='2.167.0')
545
-
546
- valu = await self.hive.add(path, valu)
547
- await self.msgq.put(('hive:sync', {'iden': iden}))
548
- return valu
549
-
550
- async def popAndSync(self, path, iden, nexs=False):
551
- s_common.deprecated('HiveApi.popAndSync', curv='2.167.0')
552
-
553
- valu = await self.hive.pop(path, nexs=nexs)
554
- await self.msgq.put(('hive:sync', {'iden': iden}))
555
- return valu
556
-
557
- async def _onHapiFini(self):
558
- await self.msgq.put(None)
559
-
560
- async def _onHiveEdit(self, mesg):
561
- self.msgq.put_nowait(mesg)
562
-
563
- async def get(self, full):
564
- s_common.deprecated('HiveApi.get', curv='2.167.0')
565
- return await self.hive.get(full)
566
-
567
- async def edits(self):
568
- s_common.deprecated('HiveApi.edits', curv='2.167.0')
569
-
570
- while not self.isfini:
571
-
572
- item = await self.msgq.get()
573
- if item is None:
574
- return
575
-
576
- yield item
577
-
578
- class TeleHive(Hive):
579
- '''
580
- A Hive that acts as a consistent read cache for a telepath proxy Hive
581
- '''
582
-
583
- async def __anit__(self, proxy):
584
-
585
- self.proxy = proxy
586
-
587
- await Hive.__anit__(self)
588
-
589
- self.lock = asyncio.Lock()
590
-
591
- self.syncevents = {} # iden: asyncio.Event()
592
-
593
- # fire a task to sync the sections of the tree we open
594
- self.schedCoro(self._runHiveLoop())
595
-
596
- self.mesgbus = await s_base.Base.anit()
597
- self.mesgbus.on('hive:set', self._onHiveSet)
598
- self.mesgbus.on('hive:pop', self._onHivePop)
599
- self.mesgbus.on('hive:tree', self._onHiveTree)
600
- self.mesgbus.on('hive:sync', self._onHiveSync)
601
-
602
- self.onfini(self.mesgbus.fini)
603
-
604
- self.onfini(proxy.fini)
605
-
606
- async def _onHiveSync(self, mesg):
607
-
608
- iden = mesg[1].get('iden')
609
- evnt = self.syncevents.pop(iden, None)
610
- if evnt is None:
611
- return
612
-
613
- evnt.set()
614
-
615
- def _getSyncIden(self):
616
- iden = s_common.guid()
617
- evnt = asyncio.Event()
618
- self.syncevents[iden] = evnt
619
- return iden, evnt
620
-
621
- async def _runHiveLoop(self):
622
- while not self.isfini:
623
- async for mesg in self.proxy.edits():
624
- await self.mesgbus.dist(mesg)
625
-
626
- async def _onHiveSet(self, mesg):
627
- path = mesg[1].get('path')
628
- valu = mesg[1].get('valu')
629
- await Hive.set(self, path, valu)
630
-
631
- async def _onHivePop(self, mesg):
632
- path = mesg[1].get('path')
633
- await Hive.pop(self, path)
634
-
635
- async def _onHiveTree(self, mesg):
636
-
637
- # get an entire tree update at once
638
- path = mesg[1].get('path')
639
- tree = mesg[1].get('tree')
640
-
641
- node = await Hive.open(self, path)
642
-
643
- todo = collections.deque([(node, path, tree)])
644
-
645
- while todo:
646
-
647
- node, path, (valu, kids) = todo.popleft()
648
-
649
- # do *not* go through the set() API
650
- node.valu = valu
651
- for name, kidt in kids.items():
652
-
653
- kidp = path + (name,)
654
- kidn = await Hive.open(self, kidp)
655
-
656
- todo.append((kidn, kidp, kidt))
657
-
658
- async def set(self, path, valu, nexs=False):
659
- iden, evnt = self._getSyncIden()
660
- valu = await self.proxy.setAndSync(path, valu, iden, nexs=nexs)
661
- await evnt.wait()
662
- return valu
663
-
664
- async def add(self, path, valu):
665
- iden, evnt = self._getSyncIden()
666
- valu = await self.proxy.addAndSync(path, valu, iden)
667
- await evnt.wait()
668
- return valu
669
-
670
- async def pop(self, path, nexs=False):
671
- iden, evnt = self._getSyncIden()
672
- valu = await self.proxy.popAndSync(path, iden, nexs=nexs)
673
- await evnt.wait()
674
- return valu
675
-
676
- async def get(self, path):
677
- return await self.proxy.get(path)
678
-
679
- async def open(self, path):
680
-
681
- # try once pre-lock for speed
682
- node = self.nodes.get(path)
683
- if node is not None:
684
- return node
685
-
686
- async with self.lock:
687
-
688
- # try again with lock to avoid race
689
- node = self.nodes.get(path)
690
- if node is not None:
691
- return node
692
-
693
- iden, evnt = self._getSyncIden()
694
-
695
- await self.proxy.treeAndSync(path, iden)
696
-
697
- await evnt.wait()
698
-
699
- return self.nodes.get(path)
700
-
701
442
  class HiveDict(s_base.Base):
702
443
  '''
703
444
  '''
@@ -758,11 +499,6 @@ def iterpath(path):
758
499
  for i in range(len(path)):
759
500
  yield path[:i + 1]
760
501
 
761
- async def openurl(url, **opts):
762
- s_common.deprecated('synapse.lib.hive.openurl()', curv='2.198.0', eolv='2.199.0')
763
- prox = await s_telepath.openurl(url, **opts)
764
- return await TeleHive.anit(prox)
765
-
766
502
  async def opendir(dirn, conf=None):
767
503
  slab = await s_slab.Slab.anit(dirn, map_size=s_const.gibibyte)
768
504
  db = slab.initdb('hive')
synapse/lib/snap.py CHANGED
@@ -1,6 +1,5 @@
1
1
  from __future__ import annotations
2
2
 
3
- import sys
4
3
  import types
5
4
  import asyncio
6
5
  import logging
@@ -12,14 +11,12 @@ import synapse.exc as s_exc
12
11
  import synapse.common as s_common
13
12
 
14
13
  import synapse.lib.base as s_base
15
- import synapse.lib.coro as s_coro
16
14
  import synapse.lib.node as s_node
17
15
  import synapse.lib.time as s_time
18
16
  import synapse.lib.cache as s_cache
19
17
  import synapse.lib.layer as s_layer
20
18
  import synapse.lib.storm as s_storm
21
19
  import synapse.lib.types as s_types
22
- import synapse.lib.spooled as s_spooled
23
20
 
24
21
  logger = logging.getLogger(__name__)
25
22
 
@@ -138,16 +135,30 @@ class ProtoNode:
138
135
 
139
136
  if not await self.ctx.snap.hasNodeEdge(self.buid, verb, s_common.uhex(n2iden)):
140
137
  self.edges.add(tupl)
141
-
142
138
  if len(self.edges) >= 1000:
143
- nodeedits = self.ctx.getNodeEdits()
144
- await self.ctx.snap.applyNodeEdits(nodeedits)
145
- self.edges.clear()
146
-
139
+ await self.flushEdits()
147
140
  return True
148
141
 
149
142
  return False
150
143
 
144
+ async def flushEdits(self):
145
+ if (nodeedit := self.getNodeEdit()) is not None:
146
+ nodecache = {self.buid: self.node}
147
+ nodes = await self.ctx.snap.applyNodeEdits((nodeedit,), nodecache=nodecache, meta=self.ctx.meta)
148
+
149
+ if self.node is None:
150
+ if nodes and nodes[0].buid == self.buid:
151
+ self.node = nodes[0]
152
+ else: # pragma: no cover
153
+ self.node = await self.ctx.snap.getNodeByBuid(self.buid)
154
+
155
+ self.tags.clear()
156
+ self.props.clear()
157
+ self.tagprops.clear()
158
+ self.edges.clear()
159
+ self.edgedels.clear()
160
+ self.nodedata.clear()
161
+
151
162
  async def delEdge(self, verb, n2iden):
152
163
 
153
164
  if not isinstance(verb, str):
@@ -175,12 +186,8 @@ class ProtoNode:
175
186
 
176
187
  if await self.ctx.snap.layers[-1].hasNodeEdge(self.buid, verb, s_common.uhex(n2iden)):
177
188
  self.edgedels.add(tupl)
178
-
179
189
  if len(self.edgedels) >= 1000:
180
- nodeedits = self.ctx.getNodeEdits()
181
- await self.ctx.snap.applyNodeEdits(nodeedits)
182
- self.edgedels.clear()
183
-
190
+ await self.flushEdits()
184
191
  return True
185
192
 
186
193
  return False
@@ -429,6 +436,9 @@ class ProtoNode:
429
436
  full = f'{prop.name}:{subname}'
430
437
  subprop = self.form.props.get(full)
431
438
  if subprop is not None and not subprop.locked:
439
+ if subprop.deprecated:
440
+ self.ctx.snap._skipPropDeprWarn(subprop.full)
441
+
432
442
  await self.set(full, subvalu)
433
443
 
434
444
  propadds = norminfo.get('adds')
@@ -438,11 +448,14 @@ class ProtoNode:
438
448
 
439
449
  return True
440
450
 
441
- async def getSetOps(self, name, valu, norminfo=None):
451
+ async def getSetSubOps(self, name, valu, norminfo=None):
442
452
  prop = self.form.props.get(name)
443
- if prop is None:
453
+ if prop is None or prop.locked:
444
454
  return ()
445
455
 
456
+ if prop.deprecated:
457
+ self.ctx.snap._skipPropDeprWarn(prop.full)
458
+
446
459
  retn = await self._set(prop, valu, norminfo=norminfo)
447
460
  if retn is False:
448
461
  return ()
@@ -459,9 +472,7 @@ class ProtoNode:
459
472
  if propsubs is not None:
460
473
  for subname, subvalu in propsubs.items():
461
474
  full = f'{prop.name}:{subname}'
462
- subprop = self.form.props.get(full)
463
- if subprop is not None and not subprop.locked:
464
- ops.append(self.getSetOps(full, subvalu))
475
+ ops.append(self.getSetSubOps(full, subvalu))
465
476
 
466
477
  propadds = norminfo.get('adds')
467
478
  if propadds is not None:
@@ -474,7 +485,8 @@ class SnapEditor:
474
485
  '''
475
486
  A SnapEditor allows tracking node edits with subs/deps as a transaction.
476
487
  '''
477
- def __init__(self, snap):
488
+ def __init__(self, snap, meta=None):
489
+ self.meta = meta
478
490
  self.snap = snap
479
491
  self.protonodes = {}
480
492
  self.maxnodes = snap.core.maxnodes
@@ -492,6 +504,19 @@ class SnapEditor:
492
504
  nodeedits.append(nodeedit)
493
505
  return nodeedits
494
506
 
507
+ async def flushEdits(self):
508
+ nodecache = {}
509
+ nodeedits = []
510
+ for protonode in self.protonodes.values():
511
+ if (nodeedit := protonode.getNodeEdit()) is not None:
512
+ nodeedits.append(nodeedit)
513
+ nodecache[protonode.buid] = protonode.node
514
+
515
+ if nodeedits:
516
+ await self.snap.applyNodeEdits(nodeedits, nodecache=nodecache, meta=self.meta)
517
+
518
+ self.protonodes.clear()
519
+
495
520
  async def _addNode(self, form, valu, props=None, norminfo=None):
496
521
 
497
522
  self.snap.core._checkMaxNodes()
@@ -568,7 +593,7 @@ class SnapEditor:
568
593
  subs = norminfo.get('subs')
569
594
  if subs is not None:
570
595
  for prop, valu in subs.items():
571
- ops.append(protonode.getSetOps(prop, valu))
596
+ ops.append(protonode.getSetSubOps(prop, valu))
572
597
 
573
598
  adds = norminfo.get('adds')
574
599
  if adds is not None:
@@ -604,7 +629,7 @@ class SnapEditor:
604
629
  subs = norminfo.get('subs')
605
630
  if subs is not None:
606
631
  for prop, valu in subs.items():
607
- ops.append(protonode.getSetOps(prop, valu))
632
+ ops.append(protonode.getSetSubOps(prop, valu))
608
633
 
609
634
  while ops:
610
635
  oset = ops.popleft()
@@ -861,6 +886,10 @@ class Snap(s_base.Base):
861
886
  self._warnonce_keys.add(mesg)
862
887
  await self.warn(mesg, log, **info)
863
888
 
889
+ def _skipPropDeprWarn(self, name):
890
+ mesg = f'The property {name} is deprecated or using a deprecated type and will be removed in 3.0.0'
891
+ self._warnonce_keys.add(mesg)
892
+
864
893
  async def getNodeByBuid(self, buid):
865
894
  '''
866
895
  Retrieve a node tuple by binary id.
@@ -1218,11 +1247,13 @@ class Snap(s_base.Base):
1218
1247
  if nodes:
1219
1248
  return nodes[0]
1220
1249
 
1221
- async def applyNodeEdits(self, edits, nodecache=None):
1250
+ async def applyNodeEdits(self, edits, nodecache=None, meta=None):
1222
1251
  '''
1223
1252
  Sends edits to the write layer and evaluates the consequences (triggers, node object updates)
1224
1253
  '''
1225
- meta = await self.getSnapMeta()
1254
+ if meta is None:
1255
+ meta = await self.getSnapMeta()
1256
+
1226
1257
  saveoff, changes, nodes = await self._applyNodeEdits(edits, meta, nodecache=nodecache)
1227
1258
  return nodes
1228
1259
 
@@ -1572,7 +1603,8 @@ class Snap(s_base.Base):
1572
1603
  proptype = prop.type
1573
1604
  for prop in prop.getAlts():
1574
1605
  if prop.type.isarray and prop.type.arraytype == proptype:
1575
- if valu in node.get(prop.name):
1606
+ arryvalu = node.get(prop.name)
1607
+ if arryvalu is not None and valu in arryvalu:
1576
1608
  return True
1577
1609
  else:
1578
1610
  if node.get(prop.name) == valu:
@@ -1660,10 +1692,6 @@ class Snap(s_base.Base):
1660
1692
  return tagnode
1661
1693
 
1662
1694
  async def _raiseOnStrict(self, ctor, mesg, **info):
1663
- if __debug__:
1664
- if issubclass(ctor, s_exc.IsDeprLocked):
1665
- sys.audit('synapse.exc.IsDeprLocked', (mesg, info))
1666
-
1667
1695
  if self.strict:
1668
1696
  raise ctor(mesg=mesg, **info)
1669
1697
  await self.warn(mesg)
synapse/lib/storm.py CHANGED
@@ -2077,7 +2077,7 @@ class Runtime(s_base.Base):
2077
2077
 
2078
2078
  class Parser:
2079
2079
 
2080
- def __init__(self, prog=None, descr=None, root=None, model=None):
2080
+ def __init__(self, prog=None, descr=None, root=None, model=None, cdef=None):
2081
2081
 
2082
2082
  if root is None:
2083
2083
  root = self
@@ -2088,6 +2088,7 @@ class Parser:
2088
2088
 
2089
2089
  self.prog = prog
2090
2090
  self.descr = descr
2091
+ self.cdef = cdef
2091
2092
 
2092
2093
  self.exc = None
2093
2094
 
@@ -2365,6 +2366,21 @@ class Parser:
2365
2366
 
2366
2367
  self._printf(f'Usage: {self.prog} [options] {posargs}')
2367
2368
 
2369
+ if self.cdef is not None and (endpoints := self.cdef.get('endpoints')):
2370
+ self._printf('')
2371
+ self._printf('Endpoints:')
2372
+ self._printf('')
2373
+ base_w = 32
2374
+ wrap_w = 120 - base_w
2375
+ for endpoint in endpoints:
2376
+ path = endpoint['path']
2377
+ desc = endpoint.get('desc', '')
2378
+ base = f' {path}'
2379
+ wrap_desc = self._wrap_text(desc, wrap_w) if desc else ['']
2380
+ self._printf(f'{base:<{base_w-2}}: {wrap_desc[0]}')
2381
+ for ln in wrap_desc[1:]:
2382
+ self._printf(f'{"":<{base_w}}{ln}')
2383
+
2368
2384
  options = [x for x in self.allargs if x[0][0].startswith('-')]
2369
2385
 
2370
2386
  self._printf('')
@@ -2655,6 +2671,7 @@ class PureCmd(Cmd):
2655
2671
  if inputs:
2656
2672
  pars.set_inputs(inputs)
2657
2673
 
2674
+ pars.cdef = self.cdef
2658
2675
  return pars
2659
2676
 
2660
2677
  async def execStormCmd(self, runt, genr):
@@ -3653,23 +3670,26 @@ class MergeCmd(Cmd):
3653
3670
 
3654
3671
  genr = diffgenr()
3655
3672
 
3656
- async with await runt.snap.view.parent.snap(user=runt.user.iden) as snap:
3673
+ async with await runt.snap.view.parent.snap(user=runt.user) as snap:
3657
3674
  snap.strict = False
3658
3675
 
3659
3676
  snap.on('warn', runt.snap.dist)
3660
3677
 
3678
+ meta = {'user': runt.user.iden}
3679
+
3680
+ if doapply:
3681
+ editor = s_snap.SnapEditor(snap, meta=meta)
3682
+
3661
3683
  async for node, path in genr:
3662
3684
 
3663
3685
  # the timestamp for the adds/subs of each node merge will match
3664
3686
  nodeiden = node.iden()
3665
- meta = {'user': runt.user.iden, 'time': s_common.now()}
3687
+
3688
+ meta['time'] = s_common.now()
3666
3689
 
3667
3690
  sodes = await node.getStorNodes()
3668
3691
  sode = sodes[0]
3669
3692
 
3670
- if doapply:
3671
- editor = s_snap.SnapEditor(snap)
3672
-
3673
3693
  subs = []
3674
3694
 
3675
3695
  # check all node perms first
@@ -3818,13 +3838,11 @@ class MergeCmd(Cmd):
3818
3838
  subs.append((s_layer.EDIT_NODE_DEL, valu, ()))
3819
3839
 
3820
3840
  if doapply:
3821
- addedits = editor.getNodeEdits()
3822
- if addedits:
3823
- await runt.snap.view.parent.storNodeEdits(addedits, meta=meta)
3841
+ await editor.flushEdits()
3824
3842
 
3825
3843
  if subs:
3826
3844
  subedits = [(node.buid, node.form.name, subs)]
3827
- await runt.snap.view.storNodeEdits(subedits, meta=meta)
3845
+ await runt.snap.applyNodeEdits(subedits, nodecache={node.buid: node}, meta=meta)
3828
3846
 
3829
3847
  runt.snap.clearCachedNode(node.buid)
3830
3848
  yield await runt.snap.getNodeByBuid(node.buid), path
synapse/lib/version.py CHANGED
@@ -223,6 +223,6 @@ def reqVersion(valu, reqver,
223
223
  ##############################################################################
224
224
  # The following are touched during the release process by bumpversion.
225
225
  # Do not modify these directly.
226
- version = (2, 198, 0)
226
+ version = (2, 200, 0)
227
227
  verstring = '.'.join([str(x) for x in version])
228
- commit = '047e5e996d0bcaad4ada4ea390a726aca1abba9b'
228
+ commit = 'ab9ca513fe33d52df7a3122c0bf4eab6951b53d3'
synapse/lib/view.py CHANGED
@@ -1474,7 +1474,8 @@ class View(s_nexus.Pusher): # type: ignore
1474
1474
 
1475
1475
  meta = await snap.getSnapMeta()
1476
1476
  async for nodeedits in fromlayr.iterLayerNodeEdits():
1477
- await self.parent.storNodeEdits([nodeedits], meta)
1477
+ meta['time'] = s_common.now()
1478
+ await snap.saveNodeEdits([nodeedits], meta)
1478
1479
 
1479
1480
  async def swapLayer(self):
1480
1481
  oldlayr = self.layers[0]