synapse 2.206.0__py311-none-any.whl → 2.208.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 (37) hide show
  1. synapse/axon.py +4 -4
  2. synapse/cortex.py +12 -3
  3. synapse/lib/boss.py +5 -1
  4. synapse/lib/modelrev.py +27 -4
  5. synapse/lib/storm.lark +2 -2
  6. synapse/lib/storm.py +5 -5
  7. synapse/lib/stormhttp.py +6 -6
  8. synapse/lib/stormlib/auth.py +1 -1
  9. synapse/lib/stormlib/cortex.py +1 -1
  10. synapse/lib/stormlib/vault.py +1 -1
  11. synapse/lib/stormsvc.py +1 -1
  12. synapse/lib/stormtypes.py +64 -37
  13. synapse/lib/task.py +7 -1
  14. synapse/lib/time.py +8 -2
  15. synapse/lib/version.py +2 -2
  16. synapse/lib/view.py +6 -1
  17. synapse/tests/test_axon.py +10 -0
  18. synapse/tests/test_cortex.py +4 -4
  19. synapse/tests/test_datamodel.py +1 -1
  20. synapse/tests/test_lib_ast.py +5 -5
  21. synapse/tests/test_lib_boss.py +2 -2
  22. synapse/tests/test_lib_nexus.py +5 -11
  23. synapse/tests/test_lib_storm.py +83 -6
  24. synapse/tests/test_lib_stormhttp.py +21 -8
  25. synapse/tests/test_lib_stormlib_cache.py +2 -2
  26. synapse/tests/test_lib_stormlib_scrape.py +1 -1
  27. synapse/tests/test_lib_stormlib_storm.py +63 -0
  28. synapse/tests/test_lib_stormsvc.py +3 -0
  29. synapse/tests/test_lib_stormtypes.py +44 -9
  30. synapse/tests/test_lib_trigger.py +3 -4
  31. synapse/tests/test_lib_view.py +3 -3
  32. synapse/tests/test_tools_csvtool.py +1 -1
  33. {synapse-2.206.0.dist-info → synapse-2.208.0.dist-info}/METADATA +6 -6
  34. {synapse-2.206.0.dist-info → synapse-2.208.0.dist-info}/RECORD +37 -37
  35. {synapse-2.206.0.dist-info → synapse-2.208.0.dist-info}/WHEEL +0 -0
  36. {synapse-2.206.0.dist-info → synapse-2.208.0.dist-info}/licenses/LICENSE +0 -0
  37. {synapse-2.206.0.dist-info → synapse-2.208.0.dist-info}/top_level.txt +0 -0
synapse/axon.py CHANGED
@@ -1659,7 +1659,7 @@ class Axon(s_cell.Cell):
1659
1659
  'code': resp.status,
1660
1660
  'body': await resp.read(),
1661
1661
  'reason': s_common.httpcodereason(resp.status),
1662
- 'headers': dict(resp.headers),
1662
+ 'headers': {str(k): v for k, v in resp.headers.items()},
1663
1663
  }
1664
1664
  return info
1665
1665
 
@@ -1706,7 +1706,7 @@ class Axon(s_cell.Cell):
1706
1706
  'url': str(resp.url),
1707
1707
  'code': resp.status,
1708
1708
  'reason': s_common.httpcodereason(resp.status),
1709
- 'headers': dict(resp.headers),
1709
+ 'headers': {str(k): v for k, v in resp.headers.items()},
1710
1710
  }
1711
1711
  return info
1712
1712
 
@@ -1736,11 +1736,11 @@ class Axon(s_cell.Cell):
1736
1736
  'ok': True,
1737
1737
  'url': str(resp.real_url),
1738
1738
  'code': resp.status,
1739
- 'headers': dict(resp.headers),
1739
+ 'headers': {str(k): v for k, v in resp.headers.items()},
1740
1740
  'reason': s_common.httpcodereason(resp.status),
1741
1741
  'request': {
1742
1742
  'url': str(resp.request_info.real_url),
1743
- 'headers': dict(resp.request_info.headers),
1743
+ 'headers': {str(k): v for k, v in resp.request_info.headers.items()},
1744
1744
  'method': str(resp.request_info.method),
1745
1745
  }
1746
1746
  }
synapse/cortex.py CHANGED
@@ -1581,6 +1581,9 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
1581
1581
  for layer in self.layers.values():
1582
1582
  await layer.initLayerActive()
1583
1583
 
1584
+ for pkgdef in list(self.stormpkgs.values()):
1585
+ self._runStormPkgOnload(pkgdef)
1586
+
1584
1587
  self.runActiveTask(_runMigrations())
1585
1588
 
1586
1589
  await self.initStormPool()
@@ -2638,6 +2641,7 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
2638
2641
  return
2639
2642
 
2640
2643
  self.loadStormPkg(pkgdef)
2644
+ self._runStormPkgOnload(pkgdef)
2641
2645
  self.pkgdefs.set(name, pkgdef)
2642
2646
 
2643
2647
  self._clearPermDefs()
@@ -2929,9 +2933,13 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
2929
2933
  self._initEasyPerm(gdef)
2930
2934
  self.pkggraphs[gdef['iden']] = gdef
2931
2935
 
2936
+ def _runStormPkgOnload(self, pkgdef):
2937
+ name = pkgdef.get('name')
2932
2938
  onload = pkgdef.get('onload')
2939
+
2933
2940
  if onload is not None and self.isactive:
2934
2941
  async def _onload():
2942
+ await self.fire('core:pkg:onload:start', pkg=name)
2935
2943
  try:
2936
2944
  async for mesg in self.storm(onload):
2937
2945
  if mesg[0] == 'print':
@@ -2943,10 +2951,11 @@ class Cortex(s_oauth.OAuthMixin, s_cell.Cell): # type: ignore
2943
2951
  await asyncio.sleep(0)
2944
2952
  except asyncio.CancelledError: # pragma: no cover
2945
2953
  raise
2946
- except Exception: # pragma: no cover
2947
- logger.warning(f'onload failed for package: {name}')
2954
+ except Exception as exc: # pragma: no cover
2955
+ logger.warning(f'onload failed for package: {name}', exc_info=exc)
2948
2956
  await self.fire('core:pkg:onload:complete', pkg=name)
2949
- self.schedCoro(_onload())
2957
+
2958
+ self.runActiveTask(_onload())
2950
2959
 
2951
2960
  # N.B. This function is intentionally not async in order to prevent possible user race conditions for code
2952
2961
  # executing outside of the nexus lock.
synapse/lib/boss.py CHANGED
@@ -10,6 +10,10 @@ class Boss(s_base.Base):
10
10
 
11
11
  '''
12
12
  An object to track "promoted" async tasks.
13
+
14
+ Promoted tasks are asyncio tasks, wrapped in a synapse task
15
+ (``s_task.Task``), that are visible to storm users via the task tracking
16
+ libs/commands such as ``ps.list`` and ``$lib.ps.list()``.
13
17
  '''
14
18
  async def __anit__(self):
15
19
  await s_base.Base.__anit__(self)
@@ -45,7 +49,7 @@ class Boss(s_base.Base):
45
49
 
46
50
  async def promotetask(self, task, name, user, info=None, taskiden=None):
47
51
 
48
- synt = getattr(task, '_syn_task', None)
52
+ synt = s_task.syntask(task)
49
53
 
50
54
  if synt is not None:
51
55
 
synapse/lib/modelrev.py CHANGED
@@ -1466,6 +1466,7 @@ class ModelMigration_0_2_31:
1466
1466
  if (v2_2 := props.get('v2_2')) is not None:
1467
1467
  propvalu, stortype = v2_2
1468
1468
  if not s_infotech.isValidCpe22(propvalu):
1469
+ logger.debug(f'Queueing invalid v2_2 value for deletion iden={s_common.ehex(buid)} valu={propvalu}')
1469
1470
  await self._queueEdit(
1470
1471
  layer.iden,
1471
1472
  (buid, 'it:sec:cpe', (
@@ -1622,10 +1623,25 @@ class ModelMigration_0_2_31:
1622
1623
  if propvalu is not None:
1623
1624
  break
1624
1625
 
1625
- newvalu, _ = form.type.norm(propvalu)
1626
- await self.moveNode(buid, newvalu)
1626
+ if propvalu is None: # pragma: no cover
1627
+ # We didn't find a v2_2 value so remove this node
1628
+ await self.removeNode(buid)
1629
+ removed += 1
1630
+
1631
+ else:
1632
+ # We did find a v2_2 value so try to norm it and use that new value to move the node. If this fails,
1633
+ # remove the node.
1634
+ try:
1635
+ newvalu, _ = form.type.norm(propvalu)
1627
1636
 
1628
- migrated += 1
1637
+ except s_exc.BadTypeValu: # pragma: no cover
1638
+ logger.debug('Unexpectedly encountered invalid v2_2 prop: iden=%s valu=%s', s_common.ehex(buid), propvalu)
1639
+ await self.removeNode(buid)
1640
+ removed += 1
1641
+
1642
+ else:
1643
+ await self.moveNode(buid, newvalu)
1644
+ migrated += 1
1629
1645
 
1630
1646
  elif action == 'remove':
1631
1647
  newvalu = None
@@ -1636,10 +1652,17 @@ class ModelMigration_0_2_31:
1636
1652
  if propvalu is None:
1637
1653
  continue
1638
1654
 
1639
- newvalu, _ = form.type.norm(propvalu)
1640
1655
  # This prop is going to be the new primary value so delete the secondary prop
1641
1656
  await self.editPropDel(layriden, buid, 'it:sec:cpe', 'v2_2', propvalu, stortype)
1642
1657
 
1658
+ # We did find a v2_2 value so try to norm it and use that new value to move the node. If this fails,
1659
+ # remove the node.
1660
+ try:
1661
+ newvalu, _ = form.type.norm(propvalu)
1662
+ except s_exc.BadTypeValu: # pragma: no cover
1663
+ logger.debug('Unexpectedly encountered invalid v2_2 prop: iden=%s valu=%s', s_common.ehex(buid), propvalu)
1664
+ continue
1665
+
1643
1666
  # Oh yeah! Migrate the node instead of removing it
1644
1667
  await self.moveNode(buid, newvalu)
1645
1668
 
synapse/lib/storm.lark CHANGED
@@ -5,7 +5,7 @@
5
5
  %ignore CPPCOMMENT
6
6
  %ignore WS
7
7
 
8
- WS: /[\s]/+
8
+ WS.-1: /[\s]/+
9
9
 
10
10
  // C comment: /* */
11
11
  // From https://stackoverflow.com/a/36328890/6518334
@@ -466,7 +466,7 @@ TRIPLEQUOTEDSTRING: /'''.*?'''/s
466
466
  DOUBLEQUOTEDSTRING: ESCAPED_STRING
467
467
  SINGLEQUOTEDSTRING: /'[^']*'(?!')/
468
468
 
469
- FORMATTEXT: /[^`{].*?(?<!\\)(\\\\)*?(?=[`{])/s
469
+ FORMATTEXT.-1: /((?<!\\)(\\\\)*\\[`{]|[^`{])+/s
470
470
  _formatexpr: "{" expror "}"
471
471
  formatstring: "`" (_formatexpr | FORMATTEXT)* "`"
472
472
 
synapse/lib/storm.py CHANGED
@@ -507,7 +507,7 @@ stormcmds = (
507
507
  if ( $defv = $lib.null ) {
508
508
  $defv = $lib.false
509
509
  }
510
- $text = `{$lib.str.join('.', $permdef.perm).ljust(32)} : {$permdef.desc} ( default: {$defv} )`
510
+ $text = `{('.').join($permdef.perm).ljust(32)} : {$permdef.desc} ( default: {$defv} )`
511
511
  $lib.print($text)
512
512
  }
513
513
  } else {
@@ -594,7 +594,7 @@ stormcmds = (
594
594
  $ssl = $lib.true
595
595
  if $cmdopts.ssl_noverify { $ssl = $lib.false }
596
596
 
597
- $headers = ({'X-Synapse-Version': $lib.str.join('.', $lib.version.synapse())})
597
+ $headers = ({'X-Synapse-Version': ('.').join($lib.version.synapse())})
598
598
 
599
599
  $resp = $lib.inet.http.get($cmdopts.url, ssl_verify=$ssl, headers=$headers)
600
600
 
@@ -629,7 +629,7 @@ stormcmds = (
629
629
  $synv = $lib.version.synapse()
630
630
 
631
631
  if $synv {
632
- $synv = $lib.str.join('.', $synv)
632
+ $synv = ('.').join($synv)
633
633
  }
634
634
 
635
635
  if $comm {
@@ -1078,8 +1078,8 @@ stormcmds = (
1078
1078
  $lib.print('entries: incunit incval required')
1079
1079
 
1080
1080
  for $rec in $job.recs {
1081
- $incunit = $lib.str.format('{incunit}', incunit=$rec.incunit).ljust(10)
1082
- $incval = $lib.str.format('{incval}', incval=$rec.incval).ljust(6)
1081
+ $incunit = (`{$rec.incunit}`).ljust(10)
1082
+ $incval = (`{$rec.incval}`).ljust(6)
1083
1083
 
1084
1084
  $lib.print(' {incunit} {incval} {reqdict}',
1085
1085
  incunit=$incunit, incval=$incval, reqdict=$rec.reqdict)
synapse/lib/stormhttp.py CHANGED
@@ -457,23 +457,23 @@ class LibHttp(s_stormtypes.Lib):
457
457
  hnfo = {
458
458
  'code': hist.status,
459
459
  'reason': await self.codereason(hist.status),
460
- 'headers': dict(hist.headers),
460
+ 'headers': {str(k): v for k, v in hist.headers.items()},
461
461
  'url': str(hist.url),
462
462
  # aiohttp has already closed the connection by this point
463
463
  # so there is no connection to read a body from.
464
464
  'body': b'',
465
465
  'history': [],
466
- 'request_headers': dict(hist.request_info.headers)
466
+ 'request_headers': {str(k): v for k, v in hist.request_info.headers.items()}
467
467
  }
468
468
  history.append(hnfo)
469
469
  info = {
470
470
  'code': resp.status,
471
471
  'reason': await self.codereason(resp.status),
472
- 'headers': dict(resp.headers),
472
+ 'headers': {str(k): v for k, v in resp.headers.items()},
473
473
  'url': str(resp.url),
474
474
  'body': await resp.read(),
475
475
  'history': history,
476
- 'request_headers': dict(resp.request_info.headers)
476
+ 'request_headers': {str(k): v for k, v in resp.request_info.headers.items()},
477
477
  }
478
478
  return HttpResp(info)
479
479
 
@@ -492,11 +492,11 @@ class LibHttp(s_stormtypes.Lib):
492
492
  'err': err,
493
493
  'code': -1,
494
494
  'reason': reason,
495
- 'headers': dict(),
495
+ 'headers': {},
496
496
  'url': url,
497
497
  'body': b'',
498
498
  'history': [],
499
- 'request_headers': dict(),
499
+ 'request_headers': {},
500
500
  }
501
501
  return HttpResp(info)
502
502
 
@@ -589,7 +589,7 @@ stormcmds = (
589
589
  'storm': '''
590
590
 
591
591
  for $pdef in $lib.auth.getPermDefs() {
592
- $perm = $lib.str.join(".", $pdef.perm)
592
+ $perm = ('.').join($pdef.perm)
593
593
 
594
594
  if $cmdopts.find {
595
595
  $find = $cmdopts.find.lower()
@@ -107,7 +107,7 @@ stormcmds = [
107
107
  if $perms {
108
108
  $lib.print('The following user permissions are required to run this HTTP API endpoint:')
109
109
  for $pdef in $perms {
110
- $perm = $lib.str.join(".", $pdef.perm)
110
+ $perm = ('.').join($pdef.perm)
111
111
  $lib.print($perm)
112
112
  $lib.print(` default: {$pdef.default}`)
113
113
  }
@@ -272,7 +272,7 @@ stormcmds = (
272
272
  $levels.append($key)
273
273
  }
274
274
 
275
- $levels = $lib.str.join(", ", $levels)
275
+ $levels = (', ').join($levels)
276
276
 
277
277
  $lib.exit(`Invalid level specified: {$cmdopts.level}. Level must be one of: {$levels}.`)
278
278
  }
synapse/lib/stormsvc.py CHANGED
@@ -68,7 +68,7 @@ stormcmds = (
68
68
  if $sname {} else { $sname = 'Unknown' }
69
69
  $svers = $sdef.svcvers
70
70
  if $svers {
71
- $svers = $lib.str.join('.', $svers)
71
+ $svers = ('.').join($svers)
72
72
  } else {
73
73
  $svers = 'Unknown'
74
74
  }
synapse/lib/stormtypes.py CHANGED
@@ -1235,7 +1235,7 @@ class LibBase(Lib):
1235
1235
  Examples:
1236
1236
  Fire an event called ``demo`` with some data::
1237
1237
 
1238
- cli> storm $foo='bar' $lib.fire('demo', foo=$foo, knight='ni')
1238
+ storm> $foo='bar' $lib.fire('demo', foo=$foo, knight='ni')
1239
1239
  ...
1240
1240
  ('storm:fire', {'type': 'demo', 'data': {'foo': 'bar', 'knight': 'ni'}})
1241
1241
  ...
@@ -1268,7 +1268,7 @@ class LibBase(Lib):
1268
1268
  Examples:
1269
1269
  Create a dictionary object with a key whose value is null, and call ``$lib.fire()`` with it::
1270
1270
 
1271
- cli> storm $d=({"key": $lib.null}) $lib.fire('demo', d=$d)
1271
+ storm> $d=({"key": $lib.null}) $lib.fire('demo', d=$d)
1272
1272
  ('storm:fire', {'type': 'demo', 'data': {'d': {'key': None}}})
1273
1273
  ''',
1274
1274
  'type': 'null', },
@@ -1295,7 +1295,7 @@ class LibBase(Lib):
1295
1295
  Examples:
1296
1296
  Conditionally print a statement based on the constant value::
1297
1297
 
1298
- cli> storm if $lib.true { $lib.print('Is True') } else { $lib.print('Is False') }
1298
+ storm> if $lib.true { $lib.print('Is True') } else { $lib.print('Is False') }
1299
1299
  Is True
1300
1300
  ''',
1301
1301
  'type': 'boolean', },
@@ -1305,10 +1305,10 @@ class LibBase(Lib):
1305
1305
  Examples:
1306
1306
  Conditionally print a statement based on the constant value::
1307
1307
 
1308
- cli> storm if $lib.false { $lib.print('Is True') } else { $lib.print('Is False') }
1308
+ storm> if $lib.false { $lib.print('Is True') } else { $lib.print('Is False') }
1309
1309
  Is False''',
1310
1310
  'type': 'boolean', },
1311
- {'name': 'text', 'desc': 'Get a Storm Text object. This is deprecated; please use a list to append strings to, and then use ``$lib.str.join()`` to join them on demand.',
1311
+ {'name': 'text', 'desc': "Get a Storm Text object. This is deprecated; please use a list to append strings to, and then use ``('.').join($listOfStr)`` to join them on demand.",
1312
1312
  'deprecated': {'eolvers': '3.0.0'},
1313
1313
  'type': {'type': 'function', '_funcname': '_text',
1314
1314
  'args': (
@@ -1344,19 +1344,19 @@ class LibBase(Lib):
1344
1344
  Examples:
1345
1345
  Print a simple string::
1346
1346
 
1347
- cli> storm $lib.print("Hello world!")
1347
+ storm> $lib.print("Hello world!")
1348
1348
  Hello world!
1349
1349
 
1350
1350
  Format and print string based on variables::
1351
1351
 
1352
- cli> storm $d=({"key1": (1), "key2": "two"})
1352
+ storm> $d=({"key1": (1), "key2": "two"})
1353
1353
  for ($key, $value) in $d { $lib.print('{k} => {v}', k=$key, v=$value) }
1354
1354
  key1 => 1
1355
1355
  key2 => two
1356
1356
 
1357
1357
  Use values off of a node to format and print string::
1358
1358
 
1359
- cli> storm inet:ipv4:asn
1359
+ storm> inet:ipv4:asn
1360
1360
  $lib.print("node: {ndef}, asn: {asn}", ndef=$node.ndef(), asn=:asn) | spin
1361
1361
  node: ('inet:ipv4', 16909060), asn: 1138
1362
1362
 
@@ -1376,7 +1376,7 @@ class LibBase(Lib):
1376
1376
  Examples:
1377
1377
  Generate a sequence of integers based on the size of an array::
1378
1378
 
1379
- cli> storm $a=(foo,bar,(2)) for $i in $lib.range($lib.len($a)) {$lib.fire('test', indx=$i, valu=$a.$i)}
1379
+ storm> $a=(foo,bar,(2)) for $i in $lib.range($lib.len($a)) {$lib.fire('test', indx=$i, valu=$a.$i)}
1380
1380
  Executing query at 2021/03/22 19:25:48.835
1381
1381
  ('storm:fire', {'type': 'test', 'data': {'index': 0, 'valu': 'foo'}})
1382
1382
  ('storm:fire', {'type': 'test', 'data': {'index': 1, 'valu': 'bar'}})
@@ -1691,7 +1691,7 @@ class LibBase(Lib):
1691
1691
  s_common.deprecated('$lib.text()', curv='2.194.0')
1692
1692
  runt = s_scope.get('runt')
1693
1693
  if runt:
1694
- await runt.snap.warnonce('$lib.text() is deprecated. Please use a list to append strings to, and then use ``$lib.str.join()`` to join them on demand.')
1694
+ await runt.snap.warnonce("$lib.text() is deprecated. Please use a list to append strings to, and then use ``('').join($listOfStr)`` to join them on demand.")
1695
1695
  valu = ''.join(args)
1696
1696
  return Text(valu)
1697
1697
 
@@ -2023,15 +2023,8 @@ class LibStr(Lib):
2023
2023
  A Storm Library for interacting with strings.
2024
2024
  '''
2025
2025
  _storm_locals = (
2026
- {'name': 'join', 'desc': '''
2027
- Join items into a string using a separator.
2028
-
2029
- Examples:
2030
- Join together a list of strings with a dot separator::
2031
-
2032
- cli> storm $foo=$lib.str.join('.', ('rep', 'vtx', 'tag')) $lib.print($foo)
2033
-
2034
- rep.vtx.tag''',
2026
+ {'name': 'join', 'desc': 'Join items into a string using a separator.',
2027
+ # 'deprecated': {'eolvers': 'v3.0.0', 'mesg': 'Use ``('').join($foo, $bar, $baz, ....)`` instead.'},
2035
2028
  'type': {'type': 'function', '_funcname': 'join',
2036
2029
  'args': (
2037
2030
  {'name': 'sepr', 'type': 'str', 'desc': 'The separator used to join strings with.', },
@@ -2039,22 +2032,14 @@ class LibStr(Lib):
2039
2032
  ),
2040
2033
  'returns': {'type': 'str', 'desc': 'The joined string.', }}},
2041
2034
  {'name': 'concat', 'desc': 'Concatenate a set of strings together.',
2035
+ # 'deprecated': {'eolvers': 'v3.0.0', 'mesg': "Use ``('').join($foo, $bar, $baz, ....)`` instead."},
2042
2036
  'type': {'type': 'function', '_funcname': 'concat',
2043
2037
  'args': (
2044
2038
  {'name': '*args', 'type': 'any', 'desc': 'Items to join together.', },
2045
2039
  ),
2046
2040
  'returns': {'type': 'str', 'desc': 'The joined string.', }}},
2047
- {'name': 'format', 'desc': '''
2048
- Format a text string.
2049
-
2050
- Examples:
2051
- Format a string with a fixed argument and a variable::
2052
-
2053
- cli> storm $list=(1,2,3,4)
2054
- $str=$lib.str.format('Hello {name}, your list is {list}!', name='Reader', list=$list)
2055
- $lib.print($str)
2056
-
2057
- Hello Reader, your list is ['1', '2', '3', '4']!''',
2041
+ {'name': 'format', 'desc': 'Format a text string.',
2042
+ # 'deprecated': {'eolvers': 'v3.0.0', 'mesg': 'Use ``$mystr.format(foo=$bar)`` instead.'},
2058
2043
  'type': {'type': 'function', '_funcname': 'format',
2059
2044
  'args': (
2060
2045
  {'name': 'text', 'type': 'str', 'desc': 'The base text string.', },
@@ -2065,6 +2050,10 @@ class LibStr(Lib):
2065
2050
  )
2066
2051
  _storm_lib_path = ('str',)
2067
2052
 
2053
+ # _lib_str_join_depr_mesg = '$lib.str.join(), use "$sepr.join($items)" instead.'
2054
+ # _lib_str_concat_depr_mesg = "$lib.str.concat(), use ('').join($foo, $bar, $baz, ....) instead."
2055
+ # _lib_str_format_depr_mesg = '$lib.str.format(), use "$mystr.format(foo=$bar)" instead.'
2056
+
2068
2057
  def getObjLocals(self):
2069
2058
  return {
2070
2059
  'join': self.join,
@@ -2074,17 +2063,29 @@ class LibStr(Lib):
2074
2063
 
2075
2064
  @stormfunc(readonly=True)
2076
2065
  async def concat(self, *args):
2066
+ # s_common.deprecated(self._lib_str_concat_depr_mesg)
2067
+ # runt = s_scope.get('runt')
2068
+ # if runt:
2069
+ # await runt.snap.warnonce(self._lib_str_concat_depr_mesg)
2077
2070
  strs = [await tostr(a) for a in args]
2078
2071
  return ''.join(strs)
2079
2072
 
2080
2073
  @stormfunc(readonly=True)
2081
2074
  async def format(self, text, **kwargs):
2075
+ # s_common.deprecated(self._lib_str_format_depr_mesg)
2076
+ # runt = s_scope.get('runt')
2077
+ # if runt:
2078
+ # await runt.snap.warnonce(self._lib_str_format_depr_mesg)
2082
2079
  text = await kwarg_format(text, **kwargs)
2083
2080
 
2084
2081
  return text
2085
2082
 
2086
2083
  @stormfunc(readonly=True)
2087
2084
  async def join(self, sepr, items):
2085
+ # s_common.deprecated(self._lib_str_join_depr_mesg)
2086
+ # runt = s_scope.get('runt')
2087
+ # if runt:
2088
+ # await runt.snap.warnonce(self._lib_str_join_depr_mesg)
2088
2089
  strs = [await tostr(item) async for item in toiter(items)]
2089
2090
  return sepr.join(strs)
2090
2091
 
@@ -2324,7 +2325,7 @@ class LibAxon(Lib):
2324
2325
  Examples:
2325
2326
  Save a base64 encoded buffer to the Axon::
2326
2327
 
2327
- cli> storm $s='dGVzdA==' $buf=$lib.base64.decode($s) ($size, $sha256)=$lib.axon.put($buf)
2328
+ storm> $s='dGVzdA==' $buf=$lib.base64.decode($s) ($size, $sha256)=$lib.axon.put($buf)
2328
2329
  $lib.print('size={size} sha256={sha256}', size=$size, sha256=$sha256)
2329
2330
 
2330
2331
  size=4 sha256=9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08''',
@@ -2340,7 +2341,7 @@ class LibAxon(Lib):
2340
2341
  Check if the Axon has a given file::
2341
2342
 
2342
2343
  # This example assumes the Axon does have the bytes
2343
- cli> storm if $lib.axon.has(9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08) {
2344
+ storm> if $lib.axon.has(9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08) {
2344
2345
  $lib.print("Has bytes")
2345
2346
  } else {
2346
2347
  $lib.print("Does not have bytes")
@@ -2812,7 +2813,7 @@ class LibBytes(Lib):
2812
2813
  Examples:
2813
2814
  Save a base64 encoded buffer to the Axon::
2814
2815
 
2815
- cli> storm $s='dGVzdA==' $buf=$lib.base64.decode($s) ($size, $sha256)=$lib.bytes.put($buf)
2816
+ storm> $s='dGVzdA==' $buf=$lib.base64.decode($s) ($size, $sha256)=$lib.bytes.put($buf)
2816
2817
  $lib.print('size={size} sha256={sha256}', size=$size, sha256=$sha256)
2817
2818
 
2818
2819
  size=4 sha256=9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08''',
@@ -2828,7 +2829,7 @@ class LibBytes(Lib):
2828
2829
  Check if the Axon has a given file::
2829
2830
 
2830
2831
  # This example assumes the Axon does have the bytes
2831
- cli> storm if $lib.bytes.has(9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08) {
2832
+ storm> if $lib.bytes.has(9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08) {
2832
2833
  $lib.print("Has bytes")
2833
2834
  } else {
2834
2835
  $lib.print("Does not have bytes")
@@ -3002,7 +3003,7 @@ class LibTime(Lib):
3002
3003
  Examples:
3003
3004
  Convert a timestamp from seconds to millis and format it::
3004
3005
 
3005
- cli> storm $seconds=1594684800 $millis=$lib.time.fromunix($seconds)
3006
+ storm> $seconds=1594684800 $millis=$lib.time.fromunix($seconds)
3006
3007
  $str=$lib.time.format($millis, '%A %d, %B %Y') $lib.print($str)
3007
3008
 
3008
3009
  Tuesday 14, July 2020''',
@@ -3017,7 +3018,7 @@ class LibTime(Lib):
3017
3018
  Examples:
3018
3019
  Parse a string as for its month/day/year value into a timestamp::
3019
3020
 
3020
- cli> storm $s='06/01/2020' $ts=$lib.time.parse($s, '%m/%d/%Y') $lib.print($ts)
3021
+ storm> $s='06/01/2020' $ts=$lib.time.parse($s, '%m/%d/%Y') $lib.print($ts)
3021
3022
 
3022
3023
  1590969600000''',
3023
3024
  'type': {'type': 'function', '_funcname': '_parse',
@@ -3034,7 +3035,7 @@ class LibTime(Lib):
3034
3035
  Examples:
3035
3036
  Format a timestamp into a string::
3036
3037
 
3037
- cli> storm $now=$lib.time.now() $str=$lib.time.format($now, '%A %d, %B %Y') $lib.print($str)
3038
+ storm> $now=$lib.time.now() $str=$lib.time.format($now, '%A %d, %B %Y') $lib.print($str)
3038
3039
 
3039
3040
  Tuesday 14, July 2020''',
3040
3041
  'type': {'type': 'function', '_funcname': '_format',
@@ -4659,6 +4660,26 @@ class Str(Prim):
4659
4660
  {'name': 'json', 'desc': 'Parse a JSON string and return the deserialized data.',
4660
4661
  'type': {'type': 'function', '_funcname': '_methStrJson', 'args': (),
4661
4662
  'returns': {'type': 'prim', 'desc': 'The JSON deserialized object.', }}},
4663
+ {'name': 'join', 'desc': '''
4664
+ Join items into a string using the current string as a separator.
4665
+
4666
+ Examples:
4667
+ Join together a list of strings with a dot separator::
4668
+
4669
+ storm> $sepr='.' $foo=$sepr.join(('rep', 'vtx', 'tag')) $lib.print($foo)
4670
+
4671
+ rep.vtx.tag
4672
+
4673
+ Join values inline together with a dot separator::
4674
+
4675
+ storm> $foo=('.').join(('rep', 'vtx', 'tag')) $lib.print($foo)
4676
+
4677
+ rep.vtx.tag''',
4678
+ 'type': {'type': 'function', '_funcname': '_methStrJoin',
4679
+ 'args': (
4680
+ {'name': 'items', 'type': 'list', 'desc': 'A list of items to join together.', },
4681
+ ),
4682
+ 'returns': {'type': 'str', 'desc': 'The joined string.', }}},
4662
4683
  )
4663
4684
  _storm_typename = 'str'
4664
4685
  _ismutable = False
@@ -4689,6 +4710,7 @@ class Str(Prim):
4689
4710
  'reverse': self._methStrReverse,
4690
4711
  'format': self._methStrFormat,
4691
4712
  'json': self._methStrJson,
4713
+ 'join': self._methStrJoin,
4692
4714
  }
4693
4715
 
4694
4716
  def __int__(self):
@@ -4808,6 +4830,11 @@ class Str(Prim):
4808
4830
  async def _methStrJson(self):
4809
4831
  return s_json.loads(self.valu)
4810
4832
 
4833
+ @stormfunc(readonly=True)
4834
+ async def _methStrJoin(self, items):
4835
+ strs = [await tostr(item) async for item in toiter(items)]
4836
+ return self.valu.join(strs)
4837
+
4811
4838
  @registry.registerType
4812
4839
  class Bytes(Prim):
4813
4840
  '''
synapse/lib/task.py CHANGED
@@ -132,12 +132,18 @@ def loop():
132
132
  except Exception:
133
133
  return None
134
134
 
135
+ def syntask(task):
136
+ '''
137
+ Return the synapse task associated with the provided asyncio task.
138
+ '''
139
+ return getattr(task, '_syn_task', None)
140
+
135
141
  def current():
136
142
  '''
137
143
  Return the current synapse task.
138
144
  '''
139
145
  task = asyncio.current_task()
140
- return getattr(task, '_syn_task', None)
146
+ return syntask(task)
141
147
 
142
148
  def user():
143
149
  '''
synapse/lib/time.py CHANGED
@@ -312,5 +312,11 @@ def toUTC(tick, fromzone):
312
312
  mesg = f'Unknown timezone: {fromzone}'
313
313
  raise s_exc.BadArg(mesg=mesg) from e
314
314
 
315
- base = datetime.datetime(1970, 1, 1, tzinfo=tz) + datetime.timedelta(milliseconds=tick)
316
- return int(base.astimezone(pytz.UTC).timestamp() * 1000)
315
+ base = datetime.datetime(1970, 1, 1) + datetime.timedelta(milliseconds=tick)
316
+ try:
317
+ localized = tz.localize(base, is_dst=None)
318
+ except pytz.exceptions.AmbiguousTimeError as e:
319
+ mesg = f'Ambiguous time: {base} {fromzone}'
320
+ raise s_exc.BadArg(mesg=mesg) from e
321
+
322
+ return int(localized.astimezone(pytz.UTC).timestamp() * 1000)
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, 206, 0)
226
+ version = (2, 208, 0)
227
227
  verstring = '.'.join([str(x) for x in version])
228
- commit = '0be7ce9e061b2c752fb454adb50c5267cc14fdfe'
228
+ commit = '2cc5a903edc504ca95e4a6b905fdb468b7e3b361'
synapse/lib/view.py CHANGED
@@ -8,6 +8,7 @@ import synapse.common as s_common
8
8
 
9
9
  import synapse.lib.cell as s_cell
10
10
  import synapse.lib.snap as s_snap
11
+ import synapse.lib.task as s_task
11
12
  import synapse.lib.layer as s_layer
12
13
  import synapse.lib.nexus as s_nexus
13
14
  import synapse.lib.scope as s_scope
@@ -1000,7 +1001,11 @@ class View(s_nexus.Pusher): # type: ignore
1000
1001
  keepalive = opts.get('keepalive')
1001
1002
  if keepalive is not None and keepalive <= 0:
1002
1003
  raise s_exc.BadArg(mesg=f'keepalive must be > 0; got {keepalive}')
1003
- synt = await self.core.boss.promote('storm', user=user, info=taskinfo, taskiden=taskiden)
1004
+
1005
+ if (synt := s_task.current()) is None:
1006
+ # we only want to promote if we aren't already a syntask because we're probably a worker task that shouldn't
1007
+ # show up in the main task list
1008
+ synt = await self.core.boss.promote('storm', user=user, info=taskinfo, taskiden=taskiden)
1004
1009
 
1005
1010
  show = opts.get('show', set())
1006
1011