async def nodesByPropValu(self, full, cmpr, valu): if cmpr == 'type=': async for node in self.nodesByPropValu(full, '=', valu): yield node async for node in self.nodesByPropTypeValu(full, valu): yield node return prop = self.core.model.prop(full) if prop is None: mesg = f'No property named "{full}".' raise s_exc.NoSuchProp(mesg=mesg) cmprvals = prop.type.getStorCmprs(cmpr, valu) # an empty return probably means ?= with invalid value if not cmprvals: return if prop.isrunt: for storcmpr, storvalu, _ in cmprvals: async for node in self.getRuntNodes(prop.full, valu=storvalu, cmpr=storcmpr): yield node return if prop.isform: for layr in self.layers: genr = layr.liftByFormValu(prop.name, cmprvals) async for node in self._joinStorGenr(layr, genr): # TODO merge sort rather than use bylayer if node.bylayer.get('ndef') != layr: continue yield node return if prop.isuniv: for layr in self.layers: genr = layr.liftByPropValu(None, prop.name, cmprvals) async for node in self._joinStorGenr(layr, genr): if node.bylayer['props'].get(prop.name) != layr: continue yield node return for layr in self.layers: genr = layr.liftByPropValu(prop.form.name, prop.name, cmprvals) async for node in self._joinStorGenr(layr, genr): if node.bylayer['props'].get(prop.name) != layr: continue yield node
async def nodesByPropArray(self, full, cmpr, valu): prop = self.core.model.prop(full) if prop is None: mesg = f'No property named "{full}".' raise s_exc.NoSuchProp(mesg=mesg) if not isinstance(prop.type, s_types.Array): mesg = f'Array synax is invalid on non array type: {prop.type.name}.' raise s_exc.BadTypeValu(mesg=mesg) cmprvals = prop.type.arraytype.getStorCmprs(cmpr, valu) if prop.isform: async for (buid, sodes) in self.core._liftByPropArray( prop.name, None, cmprvals, self.layers): node = await self._joinSodes(buid, sodes) if node is not None: yield node return formname = None if prop.form is not None: formname = prop.form.name async for (buid, sodes) in self.core._liftByPropArray( formname, prop.name, cmprvals, self.layers): node = await self._joinSodes(buid, sodes) if node is not None: yield node
async def _getPropDelEdits(self, name, init=False): prop = self.form.prop(name) if prop is None: if self.snap.strict: mesg = f'No property named {name}.' raise s_exc.NoSuchProp(mesg=mesg, name=name, form=self.form.name) await self.snap.warn(f'No Such Property: {name}') return () if not init: if prop.info.get('ro'): if self.snap.strict: raise s_exc.ReadOnlyProp(name=name) await self.snap.warn(f'Property is read-only: {name}') return () curv = self.props.get(name, s_common.novalu) if curv is s_common.novalu: return () edits = ((s_layer.EDIT_PROP_DEL, (prop.name, None, prop.type.stortype), ()), ) return edits
async def nodesByPropArray(self, full, cmpr, valu): prop = self.core.model.prop(full) if prop is None: mesg = f'No property named "{full}".' raise s_exc.NoSuchProp(mesg=mesg) if not isinstance(prop.type, s_types.Array): mesg = f'Array synax is invalid on non array type: {prop.type.name}.' raise s_exc.BadTypeValu(mesg=mesg) cmprvals = prop.type.arraytype.getStorCmprs(cmpr, valu) if prop.isform: for layr in self.layers: genr = layr.liftByPropArray(prop.name, None, cmprvals) async for node in self._joinStorGenr(layr, genr): if node.bylayer['ndef'] != layr: continue yield node return formname = None if prop.form is not None: formname = prop.form.name for layr in self.layers: genr = layr.liftByPropArray(formname, prop.name, cmprvals) async for node in self._joinStorGenr(layr, genr): if node.bylayer['props'].get(prop.name) != layr: continue yield node
async def pop(self, name, init=False): ''' Remove a property from a node and return the value ''' prop = self.form.prop(name) if prop is None: if self.snap.strict: raise s_exc.NoSuchProp(name=name, form=self.form.name) await self.snap.warn(f'No Such Property: {name}') return False if self.form.isrunt: if prop.info.get('ro'): raise s_exc.IsRuntForm( mesg='Cannot delete read-only props on runt nodes', form=self.form.full, prop=name) return await self.snap.core.runRuntPropDel(self, prop) if not init: if prop.info.get('ro'): if self.snap.strict: raise s_exc.ReadOnlyProp(name=name) await self.snap.warn(f'Property is read-only: {name}') return False curv = self.props.pop(name, s_common.novalu) if curv is s_common.novalu: return False edits = ((s_layer.EDIT_PROP_DEL, (prop.name, None, prop.type.stortype), ()), ) await self.snap.applyNodeEdit((self.buid, self.form.name, edits))
async def run(self, runt, genr): warned = False name = self.kids[1].value() prop = runt.snap.model.props.get(name) if prop is None: raise s_exc.NoSuchProp(name=name) # TODO if we are pivoting to a form, use ndef! async for node, path in genr: if self.isjoin: yield node, path valu = await self.kids[0].compute(path) if valu is None: continue # TODO cache/bypass normalization in loop! try: async for pivo in runt.snap.getNodesBy(prop.full, valu): yield pivo, path.fork(pivo) except (s_exc.BadTypeValu, s_exc.BadLiftValu) as e: if not warned: logger.warning(f'Caught error during pivot: {e.items()}') warned = True items = e.items() mesg = items.pop('mesg', '') mesg = ': '.join( (f'{e.__class__.__qualname__} [{repr(valu)}] during pivot', mesg)) await runt.snap.fire('warn', mesg=mesg, **items)
async def _getNodesByProp(self, full, valu=None, cmpr='='): prop = self.model.prop(full) if prop is None: raise s_exc.NoSuchProp(name=full) if prop.isrunt: async for node in self.getRuntNodes(full, valu, cmpr): yield node return lops = prop.getLiftOps(valu, cmpr=cmpr) if prop.isform and cmpr == '=' and valu is not None and len( lops) == 1 and lops[0][1][2][0][0] == 'eq': # Shortcut to buid lookup if primary prop = valu norm, _ = prop.type.norm(valu) node = await self.getNodeByNdef((full, norm)) if node is None: return yield node return cmpf = prop.type.getLiftHintCmpr(valu, cmpr=cmpr) async for row, node in self.getLiftNodes(lops, prop.name, cmpf): yield node
async def _methEdgeDel(self, verb, key): ''' Delete a key from the key-value store for a verb. Args: verb (str): The name of the Edge verb to remove a key from. key (str): The name of the key to remove from the key-value store. Returns: None: Returns None. ''' verb = await s_stormtypes.tostr(verb) await self._chkEdgeVerbInView(verb) key = await s_stormtypes.tostr(key) await self._chkKeyName(key) path = self.hivepath + (verb, 'extprops') kvdict = await self.runt.snap.core.getHiveKey(path) or {} oldv = kvdict.pop(key, None) if oldv is None: raise s_exc.NoSuchProp(mesg=f'Key is not set for this edge verb', verb=verb, name=key) await self.runt.snap.core.setHiveKey(path, kvdict)
async def nodesByPropValu(self, full, cmpr, valu): if cmpr == 'type=': async for node in self.nodesByPropValu(full, '=', valu): yield node async for node in self.nodesByPropTypeValu(full, valu): yield node return prop = self.core.model.prop(full) if prop is None: mesg = f'No property named "{full}".' raise s_exc.NoSuchProp(mesg=mesg) cmprvals = prop.type.getStorCmprs(cmpr, valu) # an empty return probably means ?= with invalid value if not cmprvals: return if prop.isrunt: for storcmpr, storvalu, _ in cmprvals: async for node in self.getRuntNodes(prop.full, valu=storvalu, cmpr=storcmpr): yield node return if prop.isform: found = 0 async for (buid, sodes) in self.core._liftByFormValu( prop.name, cmprvals, self.layers): node = await self._joinSodes(buid, sodes) if node is not None: found += 1 yield node return if prop.isuniv: async for (buid, sodes) in self.core._liftByPropValu( None, prop.name, cmprvals, self.layers): node = await self._joinSodes(buid, sodes) if node is not None: yield node return async for (buid, sodes) in self.core._liftByPropValu(prop.form.name, prop.name, cmprvals, self.layers): node = await self._joinSodes(buid, sodes) if node is not None: yield node
def _normPyTuple(self, valu): try: propname, propvalu = valu except Exception as e: raise s_exc.BadTypeValu(name=self.name, valu=valu, mesg=str(e)) from None prop = self.modl.prop(propname) if prop is None: raise s_exc.NoSuchProp(name=self.name, prop=propname) propnorm, info = prop.type.norm(propvalu) return (prop.full, propnorm), {'subs': {'prop': prop.full}}
async def _getNodesByProp(self, full, valu=None, cmpr='='): prop = self.model.prop(full) if prop is None: raise s_exc.NoSuchProp(name=full) if prop.isrunt: async for node in self.getRuntNodes(full, valu, cmpr): yield node else: lops = prop.getLiftOps(valu, cmpr=cmpr) cmpf = prop.type.getLiftHintCmpr(valu, cmpr=cmpr) async for row, node in self.getLiftNodes(lops, prop.name, cmpf): yield node
async def nodesByProp(self, full): prop = self.core.model.prop(full) if prop is None: mesg = f'No property named "{full}".' raise s_exc.NoSuchProp(mesg=mesg) if prop.isrunt: async for node in self.getRuntNodes(prop.full): yield node return if prop.isform: for layr in self.layers: genr = layr.liftByProp(prop.name, None) async for node in self._joinStorGenr(layr, genr): # TODO merge sort rather than use bylayer if node.bylayer.get('ndef') != layr: continue yield node return if prop.isuniv: for layr in self.layers: genr = layr.liftByProp(None, prop.name) async for node in self._joinStorGenr(layr, genr): # TODO should these type of filters yield? if node.bylayer['props'].get(prop.name) != layr: continue yield node return formname = None if not prop.isuniv: formname = prop.form.name # Prop is secondary prop for layr in self.layers: genr = layr.liftByProp(formname, prop.name) async for node in self._joinStorGenr(layr, genr): if node.bylayer['props'].get(prop.name) != layr: continue yield node
async def run(self, runt, genr): name = self.kids[0].value() univ = runt.snap.model.props.get(name) if univ is None: raise s_exc.NoSuchProp(name=name) async for node, path in genr: runt.allowed('prop:del', name) await node.pop(name) yield node, path
def repr(self, name=None, defv=None): if name is None: return self.form.type.repr(self.ndef[1]) prop = self.form.props.get(name) if prop is None: raise s_exc.NoSuchProp(form=self.form.name, prop=name) valu = self.props.get(name) if valu is None: return defv return prop.type.repr(valu)
def repr(self, name=None, defv=None): if name is None: return self.form.type.repr(self.ndef[1]) prop = self.form.props.get(name) if prop is None: mesg = f'No property named {name}.' raise s_exc.NoSuchProp(mesg=mesg, form=self.form.name, prop=name) valu = self.props.get(name) if valu is None: return defv return prop.type.repr(valu)
async def run(self, runt, genr): name = self.kids[0].value() async for node, path in genr: prop = node.form.props.get(name) if prop is None: raise s_exc.NoSuchProp(name=name, form=node.form.name) runt.allowed('prop:del', prop.full) await node.pop(name) yield node, path
async def _methEdgeDel(self, verb, key): verb = await s_stormtypes.tostr(verb) await self._chkEdgeVerbInView(verb) key = await s_stormtypes.tostr(key) await self._chkKeyName(key) path = self.hivepath + (verb, 'extprops') kvdict = await self.runt.snap.core.getHiveKey(path) or {} oldv = kvdict.pop(key, None) if oldv is None: raise s_exc.NoSuchProp(mesg=f'Key is not set for this edge verb', verb=verb, name=key) await self.runt.snap.core.setHiveKey(path, kvdict)
def delFormProp(self, formname, propname): form = self.forms.get(formname) if form is None: raise s_exc.NoSuchForm(name=formname) prop = form.props.pop(propname, None) if prop is None: raise s_exc.NoSuchProp(name=f'{formname}:{propname}') form.props.pop(prop.name, None) form.defvals.pop(prop.name, None) self.props.pop(prop.full, None) self.props.pop((form.name, prop.name), None) self.propsbytype[prop.type.name].remove(prop)
def delFormProp(self, formname, propname): form = self.forms.get(formname) if form is None: raise s_exc.NoSuchForm(name=formname) prop = form.delProp(propname) if prop is None: raise s_exc.NoSuchProp(name=f'{formname}:{propname}') if isinstance(prop.type, s_types.Array): self.arraysbytype[prop.type.arraytype.name].remove(prop) self.props.pop(prop.full, None) self.props.pop((form.name, prop.name), None) self.propsbytype[prop.type.name].remove(prop)
async def run(self, runt, genr): name = self.kids[0].value() async for node, path in genr: valu = await self.kids[1].compute(path) prop = node.form.props.get(name) if prop is None: raise s_exc.NoSuchProp(name=name, form=node.form.name) runt.allowed('prop:set', prop.full) await node.set(name, valu) yield node, path
async def nodesByProp(self, full): prop = self.core.model.prop(full) if prop is None: mesg = f'No property named "{full}".' raise s_exc.NoSuchProp(mesg=mesg) if prop.isrunt: async for node in self.getRuntNodes(prop.full): yield node return if prop.isform: async for (buid, sodes) in self.core._liftByProp(prop.name, None, self.layers): node = await self._joinSodes(buid, sodes) if node is not None: yield node return if prop.isuniv: async for (buid, sodes) in self.core._liftByProp(None, prop.name, self.layers): node = await self._joinSodes(buid, sodes) if node is not None: yield node return formname = None if not prop.isuniv: formname = prop.form.name # Prop is secondary prop async for (buid, sodes) in self.core._liftByProp(formname, prop.name, self.layers): node = await self._joinSodes(buid, sodes) if node is not None: yield node
async def pop(self, name, init=False): ''' Remove a property from a node and return the value ''' prop = self.form.prop(name) if prop is None: if self.snap.strict: raise s_exc.NoSuchProp(name=name, form=self.form.name) await self.snap.warn(f'No Such Property: {name}') return False if self.isrunt: if prop.info.get('ro'): raise s_exc.IsRuntForm( mesg='Cannot delete read-only props on runt nodes', form=self.form.full, prop=name) return await self.snap.core.runRuntPropDel(self, prop) if not init: if prop.info.get('ro'): if self.snap.strict: raise s_exc.ReadOnlyProp(name=name) await self.snap.warn(f'Property is read-only: {name}') return False curv = self.props.pop(name, s_common.novalu) if curv is s_common.novalu: return False sops = prop.getDelOps(self.buid) splice = self.snap.splice('prop:del', ndef=self.ndef, prop=prop.name, valu=curv) await self.snap.stor(sops, [splice]) await prop.wasDel(self, curv)
def getCondEval(self, runt): name = self.kids[0].value() prop = runt.snap.model.props.get(name) if prop is None: raise s_exc.NoSuchProp(name=name) if prop.isform: async def cond(node, path): return node.form.name == prop.name return cond async def cond(node, path): if node.form.name != prop.form.name: return False return node.has(prop.name) return cond
async def getNodesByArray(self, name, valu, cmpr='='): ''' Yield nodes by an array property with *items* matching <cmpr> <valu> ''' prop = self.model.props.get(name) if prop is None: mesg = f'No property named {name}.' raise s_exc.NoSuchProp(mesg=mesg) if not isinstance(prop.type, s_types.Array): mesg = f'Prop ({name}) is not an array type.' raise s_exc.BadTypeValu(mesg=mesg) iops = prop.type.arraytype.getIndxOps(valu, cmpr=cmpr) prefix = prop.pref + b'\x01' lops = (('indx', (prop.dbname, prefix, iops)), ) #TODO post-lift cmpr filter #cmpf = prop.type.getLiftHintCmpr(valu, cmpr=cmpr) async for row, node in self.getLiftNodes(lops, prop.name): yield node
def getCondEval(self, runt): name = self.kids[0].value() cmpr = self.kids[1].value() prop = runt.snap.model.props.get(name) if prop is None: raise s_exc.NoSuchProp(name=name) ctor = prop.type.getCmprCtor(cmpr) if ctor is None: raise s_exc.NoSuchCmpr(cmpr=cmpr, name=prop.type.name) if prop.isform: async def cond(node, path): if node.ndef[0] != name: return False val1 = node.ndef[1] val2 = await self.kids[2].compute(path) return ctor(val2)(val1) return cond async def cond(node, path): val1 = node.get(prop.name) if val1 is None: return False val2 = await self.kids[2].compute(path) return ctor(val2)(val1) return cond
def oper(self): ''' ''' self.ignore(whitespace) if not self.more(): self._raiseSyntaxError('unexpected end of query text') if self.nextstr('{'): return self.subquery() # some syntax elements prior to a prop/oper name... if self.nextstr('->'): return self.formpivot() if self.nextstr('-+>'): return self.formjoin() if self.nextstr('<-'): return self.formpivotin() if self.nextstr('<+-'): return self.formjoinin() if self.nextstr('##'): return self.lifttagtag() char = self.nextchar() # var list assignment # ($foo, $bar) = $baz if char == '(': varl = self.varlist() self.ignore(whitespace) self.nextmust('=') self.ignore(whitespace) valu = self.valu() return s_ast.VarListSetOper(kids=(varl, valu)) # $foo = valu var assignment if char == '$': varn = self.varname() self.ignore(whitespace) self.nextmust('=') self.ignore(whitespace) valu = self.valu() kids = (varn, valu) return s_ast.VarSetOper(kids=kids) if char in ('+', '-'): return self.filtoper() if char == '#': return self.liftbytag() # :foo:bar relative property if char == ':': prop = self.relprop() # :foo=10 here could be assignment... self.ignore(whitespace) if self.nextstr('->'): return self.proppivot(prop) if self.nextstr('-+>'): return self.propjoin(prop) if self.nextstrs('<-', '<+-'): self._raiseSyntaxError('Pivot in syntax does not currently support relative properties.') tokn = self.peek(varset) if tokn == 'for': return self.forloop() if tokn == 'switch': return self.switchcase() if tokn == 'break': self.offs += 5 return s_ast.BreakOper() if tokn == 'continue': self.offs += 8 return s_ast.ContinueOper() noff = self.offs name = self.noms(varset) if not name: self._raiseSyntaxError('unknown query syntax') if self.modelinfo.isprop(name): # before ignoring more whitespace, check for form#tag[=time] if self.nextstr('#'): tag = self.tagname() form = s_ast.Const(name) self.ignore(whitespace) kids = [form, tag] if self.nextchar() in cmprstart: kids.append(self.cmpr()) kids.append(self.valu()) return s_ast.LiftFormTag(kids=kids) self.ignore(whitespace) if self.nextchar() in cmprstart: cmpr = self.cmpr() valu = self.valu() kids = (s_ast.Const(name), cmpr, valu) return s_ast.LiftPropBy(kids=kids) # lift by prop only return s_ast.LiftProp(kids=(s_ast.Const(name),)) if name in self.stormcmds: argv = self.cmdargv() self.ignore(whitespace) # eat a trailing | from a command at the beginning if self.nextstr('|'): self.offs += 1 return s_ast.CmdOper(kids=(s_ast.Const(name), argv)) # rewind and noms until whitespace self.offs = noff tokn = self.noms(until=whitespace) ndefs = list(s_scrape.scrape(tokn)) if ndefs: return s_ast.LiftByScrape(ndefs) self.offs = noff raise s_exc.NoSuchProp(name=name)
async def run(self, runt, genr): warned = False name = self.kids[0].value() prop = runt.snap.model.props.get(name) if prop is None: raise s_exc.NoSuchProp(name=name) # -> baz:ndef if isinstance(prop.type, s_types.Ndef): async for node, path in genr: if self.isjoin: yield node, path async for pivo in runt.snap.getNodesBy(prop.full, node.ndef): yield pivo, path.fork(pivo) return if not prop.isform: # plain old pivot... async for node, path in genr: if self.isjoin: yield node, path valu = node.ndef[1] # TODO cache/bypass normalization in loop! try: async for pivo in runt.snap.getNodesBy(prop.full, valu): yield pivo, path.fork(pivo) except (s_exc.BadTypeValu, s_exc.BadLiftValu) as e: if not warned: logger.warning( f'Caught error during pivot: {e.items()}') warned = True items = e.items() mesg = items.pop('mesg', '') mesg = ': '.join(( f'{e.__class__.__qualname__} [{repr(valu)}] during pivot', mesg)) await runt.snap.fire('warn', mesg=mesg, **items) # form -> form pivot is nonsensical. Lets help out... # if dest form is a subtype of a graph "edge", use N1 automatically if isinstance(prop.type, s_types.Edge): full = prop.name + ':n1' async for node, path in genr: if self.isjoin: yield node, path async for pivo in runt.snap.getNodesBy(full, node.ndef): yield pivo, path.fork(pivo) return # form name and type name match formprop = prop destform = prop.name # TODO: both of these should be precomputed in the model @s_cache.memoize() def getsrc(form): names = [] for name, prop in form.props.items(): if prop.type.name == destform: names.append(name) return names @s_cache.memoize() def getdst(form): # formprop is really a form here... names = [] for name, prop in formprop.props.items(): if prop.type.name == form.type.name: names.append(prop.full) return names async for node, path in genr: if self.isjoin: yield node, path # <syn:tag> -> <form> is "from tags to nodes" pivot if node.form.name == 'syn:tag' and prop.isform: async for pivo in runt.snap.getNodesBy( f'{prop.name}#{node.ndef[1]}'): yield pivo, path.fork(pivo) continue # if the source node is a graph edge, use n2 if isinstance(node.form.type, s_types.Edge): n2def = node.get('n2') if n2def[0] != destform: continue pivo = await runt.snap.getNodeByNdef(node.get('n2')) yield pivo, path.fork(pivo) continue names = getsrc(node.form) if names: for name in names: valu = node.get(name) if valu is None: continue async for pivo in runt.snap.getNodesBy(prop.name, valu): yield pivo, path.fork(pivo) continue names = getdst(node.form) if names: for name in names: found = True valu = node.ndef[1] async for pivo in runt.snap.getNodesBy(name, valu): yield pivo, path.fork(pivo) continue raise s_exc.NoSuchPivot(n1=node.form.name, n2=destform)
async def _chkKeyName(self, key): if key not in self.validedgekeys: raise s_exc.NoSuchProp( mesg=f'The requested key is not valid for light edge metadata.', name=key)
async def execStormCmd(self, runt, genr): snap = runt.snap if snap.user is not None and not snap.user.admin: await snap.warn('reindex requires an admin') return # are we re-indexing a type? if self.opts.type is not None: # is the type also a form? form = snap.model.forms.get(self.opts.type) if form is not None: await snap.printf(f'reindex form: {form.name}') async for buid, norm in snap.xact.iterFormRows(form.name): await snap.stor(form.getSetOps(buid, norm)) for prop in snap.model.getPropsByType(self.opts.type): await snap.printf(f'reindex prop: {prop.full}') formname = prop.form.name async for buid, norm in snap.xact.iterPropRows( formname, prop.name): await snap.stor(prop.getSetOps(buid, norm)) return if self.opts.subs: async for node, path in genr: form, valu = node.ndef norm, info = node.form.type.norm(valu) subs = info.get('subs') if subs is not None: for subn, subv in subs.items(): if node.form.props.get(subn): await node.set(subn, subv, init=True) yield node, path return if self.opts.form_counts: await snap.printf(f'reindex form counts (full) beginning...') await snap.core._calcFormCounts() await snap.printf(f'...done') return if self.opts.fire_handler: obj = None name = None tname = None if self.opts.fire_handler.startswith('#'): name, _ = runt.snap.model.prop('syn:tag').type.norm( self.opts.fire_handler) tname = '#' + name else: obj = runt.snap.model.prop(self.opts.fire_handler) if obj is None: raise s_exc.NoSuchProp(mesg='', name=self.opts.fire_handler) async for node, path in genr: if hasattr(obj, 'wasAdded'): if node.form.full != obj.full: continue await obj.wasAdded(node) elif hasattr(obj, 'wasSet'): if obj.form.name != node.form.name: continue valu = node.get(obj.name) if valu is None: continue await obj.wasSet(node, valu) else: # We're a tag... valu = node.get(tname) if valu is None: continue await runt.snap.core.runTagAdd(node, name, valu) yield node, path return
async def set(self, name, valu, init=False): ''' Set a property on the node. Args: name (str): The name of the property. valu (obj): The value of the property. init (bool): Set to True to disable read-only enforcement Returns: (bool): True if the property was changed. ''' if self.snap.readonly: mesg = 'Cannot set property in read-only mode.' raise s_exc.IsReadOnly(mesg=mesg) prop = self.form.prop(name) if prop is None: if self.snap.strict: mesg = f'No property named {name}.' raise s_exc.NoSuchProp(mesg=mesg, name=name, form=self.form.name) await self.snap.warn(f'NoSuchProp: name={name}') return False if self.form.isrunt: if prop.info.get('ro'): mesg = 'Cannot set read-only props on runt nodes' raise s_exc.IsRuntForm(mesg=mesg, form=self.form.full, prop=name, valu=valu) await self.snap.core.runRuntPropSet(self, prop, valu) return True curv = self.props.get(name) # normalize the property value... try: norm, info = prop.type.norm(valu) except Exception as e: mesg = f'Bad property value: {prop.full}={valu!r}' return await self.snap._raiseOnStrict(s_exc.BadTypeValu, mesg, name=prop.name, valu=valu, emesg=str(e)) # do we already have the value? if curv == norm: return False await self.snap.core._callPropSetHook(self, prop, norm) if curv is not None and not init: if prop.info.get('ro'): if self.snap.strict: raise s_exc.ReadOnlyProp(name=prop.full) # not setting a set-once prop unless we are init... return False # check for type specific merging... norm = prop.type.merge(curv, norm) if curv == norm: return False props = {prop.name: norm} nodeedits = await self.snap.getNodeAdds(self.form, self.ndef[1], props, addnode=False) await self.snap.applyNodeEdits(nodeedits) return True