Skip to content

Commit

Permalink
added ability to unset vars and derefs (#1970)
Browse files Browse the repository at this point in the history
* added ability to unset vars and derefs
  • Loading branch information
invisig0th committed Nov 30, 2020
1 parent d3115c8 commit a8a6557
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 5 deletions.
18 changes: 13 additions & 5 deletions synapse/lib/ast.py
Expand Up @@ -23,7 +23,7 @@
import synapse.lib.provenance as s_provenance
import synapse.lib.stormtypes as s_stormtypes

from synapse.lib.stormtypes import tobool, toint, toprim, tostr
from synapse.lib.stormtypes import tobool, toint, toprim, tostr, undef

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -757,16 +757,24 @@ async def run(self, runt, genr):
count += 1

valu = await vkid.compute(runt, path)
if valu is undef:
runt.popVar(name)
#TODO detect which to update here
path.popVar(name)

runt.setVar(name, valu)
#TODO detect which to update here
path.setVar(name, valu)
else:
runt.setVar(name, valu)
#TODO detect which to update here
path.setVar(name, valu)

yield node, path

if count == 0 and vkid.isRuntSafe(runt):
valu = await vkid.compute(runt, None)
runt.setVar(name, valu)
if valu is undef:
runt.popVar(name)
else:
runt.setVar(name, valu)

def getRuntVars(self, runt):
yield self.kids[0].value(), self.kids[1].isRuntSafe(runt)
Expand Down
3 changes: 3 additions & 0 deletions synapse/lib/node.py
Expand Up @@ -720,6 +720,9 @@ def getVar(self, name, defv=s_common.novalu):
def setVar(self, name, valu):
self.vars[name] = valu

def popVar(self, name):
return self.vars.pop(name, s_common.novalu)

def meta(self, name, valu):
'''
Add node specific metadata to be returned with the node.
Expand Down
7 changes: 7 additions & 0 deletions synapse/lib/storm.py
Expand Up @@ -1309,6 +1309,13 @@ def setVar(self, name, valu):
self.vars[name] = valu
return

def popVar(self, name):

if self._isRootScope(name):
return self.root.popVar(name)

return self.vars.pop(name, s_common.novalu)

def addInput(self, node):
'''
Add a Node() object as input to the query runtime.
Expand Down
43 changes: 43 additions & 0 deletions synapse/lib/stormtypes.py
Expand Up @@ -28,6 +28,9 @@

logger = logging.getLogger(__name__)

class Undef: pass
undef = Undef()

class StormTypesRegistry:
def __init__(self):
self._LIBREG = {}
Expand Down Expand Up @@ -495,6 +498,7 @@ def getObjLocals(self):
'fire': self._fire,
'list': self._list,
'null': self._null,
'undef': self._undef,
'true': self._true,
'false': self._false,
'text': self._text,
Expand All @@ -506,6 +510,24 @@ def getObjLocals(self):
'import': self._libBaseImport,
}

@property
def _undef(self):
'''
This constant can be used to unset variables and derefs.
Examples:
// Unset the variable $foo
$foo = $lib.undef
// Remove a dictionary key bar
$foo.bar = $lib.undef
// Remove a list index of 0
$foo.0 = $lib.undef
'''
return undef

@property
def _true(self):
'''
Expand Down Expand Up @@ -2006,6 +2028,11 @@ async def iter(self):
return tuple(item for item in self.valu.items())

async def setitem(self, name, valu):

if valu is undef:
self.valu.pop(name, None)
return

self.valu[name] = valu

async def deref(self, name):
Expand Down Expand Up @@ -2119,6 +2146,19 @@ def __iter__(self):
for item in self.valu:
yield item

async def setitem(self, name, valu):

indx = await toint(name)

if valu is undef:
try:
self.valu.pop(indx)
except IndexError:
pass
return

self.valu[indx] = valu

async def _derefGet(self, name):
return await self._methListIndex(name)

Expand Down Expand Up @@ -2690,6 +2730,9 @@ async def deref(self, name):
raise s_exc.StormRuntimeError(mesg=mesg)

async def setitem(self, name, valu):
if valu is undef:
self.path.popVar(name)
return
self.path.setVar(name, valu)

def __iter__(self):
Expand Down
42 changes: 42 additions & 0 deletions synapse/tests/test_lib_storm.py
Expand Up @@ -192,6 +192,48 @@ async def test_lib_storm_basics(self):
resp = await core.callStorm(wget, opts=opts)
self.true(resp['ok'])

async def test_storm_undef(self):

async with self.getTestCore() as core:

# pernode variants
self.none(await core.callStorm('''
[ ps:contact = * ]
if $node {
$foo = $lib.dict()
$foo.bar = $lib.undef
return($foo.bar)
}
'''))
with self.raises(s_exc.NoSuchVar):
await core.callStorm('[ps:contact=*] $foo = $node.repr() $foo = $lib.undef return($foo)')

with self.raises(s_exc.StormRuntimeError):
await core.callStorm('''
[ps:contact=*]
$path.vars.foo = lol
$path.vars.foo = $lib.undef
return($path.vars.foo)
''')

# runtsafe variants
self.eq(('foo', 'baz'), await core.callStorm('$foo = (foo, bar, baz) $foo.1 = $lib.undef return($foo)'))
self.eq(('foo', 'bar'), await core.callStorm('$foo = (foo, bar, baz) $foo."-1" = $lib.undef return($foo)'))
self.none(await core.callStorm('$foo = $lib.dict() $foo.bar = 10 $foo.bar = $lib.undef return($foo.bar)'))
self.eq(('woot',), await core.callStorm('''
$foo = (foo, bar, baz)
$foo.0 = $lib.undef
$foo.0 = $lib.undef
$foo.0 = $lib.undef
// one extra to test the exc handler
$foo.0 = $lib.undef
$foo.append(hehe)
$foo.0 = woot
return($foo)
'''))
with self.raises(s_exc.NoSuchVar):
await core.callStorm('$foo = 10 $foo = $lib.undef return($foo)')

async def test_storm_pkg_load(self):
cont = s_common.guid()
pkg = {
Expand Down

0 comments on commit a8a6557

Please sign in to comment.