Skip to content

Commit

Permalink
figured out a better way to do telepath client side methods and gutte…
Browse files Browse the repository at this point in the history
…d the crazy meta-class builder
  • Loading branch information
invisig0th committed May 25, 2017
1 parent a975d7e commit b971223
Show file tree
Hide file tree
Showing 11 changed files with 235 additions and 100 deletions.
8 changes: 7 additions & 1 deletion synapse/async.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,16 @@ def jobret(job):
'''
err = job[1].get('err')

# populate errinfo into SynErr
info = job[1].get('errinfo')
if info == None:
info = {}

if err != None:
if err != 'NameErr':
try:
raise synerr(err)
raise synerr(err,**info)
except NameError as e:
pass
raise JobErr(job)
Expand Down
3 changes: 2 additions & 1 deletion synapse/axon.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,7 @@ class AxonMixin:
The parts of the Axon which must be executed locally in proxy cases.
( used as mixin for both Axon and AxonProxy )
'''

@s_telepath.clientside
def eatfd(self, fd):
'''
Consume the contents of a file object into the axon as a blob.
Expand Down Expand Up @@ -189,6 +189,7 @@ def eatfd(self, fd):

return retn

@s_telepath.clientside
def eatbytes(self, byts):
'''
Consume a buffer of bytes into the axon as a blob.
Expand Down
129 changes: 62 additions & 67 deletions synapse/cores/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,78 +58,13 @@ def reqstor(name,valu):
raise BadPropValu(name=name,valu=valu)
return valu

class CortexMixin:

def __init__(self):
pass

def formNodeByBytes(self, byts, stor=True, **props):
'''
Form a new file:bytes node by passing bytes and optional props.
If stor=False is specified, the cortex will create the file:bytes
node even if it is not configured with access to an axon to store
the bytes.
Example:
core.formNodeByBytes(byts,name='foo.exe')
'''

hset = s_hashset.HashSet()
hset.update(byts)

iden,info = hset.guid()

props.update(info)

if stor:

size = props.get('size')
upid = self._getAxonWants('guid',iden,size)

if upid != None:
for chun in chunks(byts,10000000):
self._addAxonChunk(upid,chun)

return self.formTufoByProp('file:bytes', iden, **props)

def formNodeByFd(self, fd, stor=True, **props):
'''
Form a new file:bytes node by passing a file object and optional props.
'''
hset = s_hashset.HashSet()
iden,info = hset.eatfd(fd)

props.update(info)


if stor:

size = props.get('size')
upid = self._getAxonWants('guid',iden,size)

# time to send it!
if upid != None:
for byts in iterfd(fd):
self._addAxonChunk(upid,byts)

node = self.formTufoByProp('file:bytes', iden, **props)

if node[1].get('file:bytes:size') == None:
self.setTufoProp(node,'size',info.get('size'))

return node

class Cortex(EventBus,DataModel,Runtime,Configable,CortexMixin,s_ingest.IngestApi):
class Cortex(EventBus,DataModel,Runtime,Configable,s_ingest.IngestApi):
'''
Top level Cortex key/valu storage object.
'''
def __init__(self, link, **conf):
Runtime.__init__(self)
EventBus.__init__(self)
CortexMixin.__init__(self)
Configable.__init__(self)

# a cortex may have a ref to an axon
Expand Down Expand Up @@ -2231,7 +2166,67 @@ def savemesg(mesg):
'''
self.savebus.link(func)

# FIXME addSyncLink()
@s_telepath.clientside
def formNodeByBytes(self, byts, stor=True, **props):
'''
Form a new file:bytes node by passing bytes and optional props.
If stor=False is specified, the cortex will create the file:bytes
node even if it is not configured with access to an axon to store
the bytes.
Example:
core.formNodeByBytes(byts,name='foo.exe')
'''

hset = s_hashset.HashSet()
hset.update(byts)

iden,info = hset.guid()

props.update(info)

if stor:

size = props.get('size')
upid = self._getAxonWants('guid',iden,size)

if upid != None:
for chun in chunks(byts,10000000):
self._addAxonChunk(upid,chun)

return self.formTufoByProp('file:bytes', iden, **props)

@s_telepath.clientside
def formNodeByFd(self, fd, stor=True, **props):
'''
Form a new file:bytes node by passing a file object and optional props.
'''
hset = s_hashset.HashSet()
iden,info = hset.eatfd(fd)

props.update(info)


if stor:

size = props.get('size')
upid = self._getAxonWants('guid',iden,size)

# time to send it!
if upid != None:
for byts in iterfd(fd):
self._addAxonChunk(upid,byts)

node = self.formTufoByProp('file:bytes', iden, **props)

if node[1].get('file:bytes:size') == None:
self.setTufoProp(node,'size',info.get('size'))

return node


def _okSetProp(self, prop):
# check for enforcement and validity of a full prop name
Expand Down
9 changes: 9 additions & 0 deletions synapse/daemon.py
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ def __init__(self, pool=None):
self.socks = {} # sockets by iden
self.shared = {} # objects provided by daemon
self.pushed = {} # objects provided by sockets
self.csides = {} # item:[ (name,path), ... ]
self.reflect = {} # objects reflect info by name

self._dmon_links = [] # list of listen links
Expand Down Expand Up @@ -433,6 +434,7 @@ def _onTelePushMesg(self, sock, mesg):

jid = mesg[1].get('jid')
name = mesg[1].get('name')
csides = mesg[1].get('csides')
reflect = mesg[1].get('reflect')

user = sock.get('syn:user')
Expand All @@ -446,6 +448,7 @@ def onfini():
sock.onfini(onfini)

self.pushed[name] = sock
self.csides[name] = csides
self.reflect[name] = reflect

return sock.tx( tufo('job:done', jid=jid) )
Expand Down Expand Up @@ -556,6 +559,7 @@ def _onTeleSynMesg(self, sock, mesg):
}

if name != None:
ret['csides'] = self.csides.get(name)
ret['reflect'] = self.reflect.get(name)

# send a nonce along for the ride in case
Expand Down Expand Up @@ -681,6 +685,10 @@ def _onTeleCallMesg(self, sock, mesg):
if func == None:
raise NoSuchMeth(meth)

if getattr(func,'_tele_clientside',False):
name = s_reflect.getMethName(func)
raise TeleClientSide(name=name)

ret = func(*args,**kwargs)

# handle generator returns specially
Expand Down Expand Up @@ -775,6 +783,7 @@ def share(self, name, item, fini=False):
'''
self.shared[name] = item
self.reflect[name] = s_reflect.getItemInfo(item)
self.csides[name] = s_telepath.getClientSides(item)

if fini:
self.onfini( item.fini )
Expand Down
12 changes: 12 additions & 0 deletions synapse/dyndeps.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,18 @@ def getDynLocal(name):

return getattr(mod,objname,None)

def getDynMeth(name):
'''
Retrieve and return an unbound method by python path.
'''
cname,fname = name.rsplit('.',1)

clas = getDynLocal(cname)
if clas is None:
return None

return getattr(clas,fname,None)

def tryDynMod(name):
'''
Dynamically import a python module or exception.
Expand Down
16 changes: 16 additions & 0 deletions synapse/exc.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,20 @@ def _getExcMsg(self):
def items(self):
return self.errinfo.items()

def get(self, name):
'''
Return a value from the errinfo dict.
Example:
try:
foothing()
except SynErr as e:
blah = e.get('blah')
'''
return self.errinfo.get(name)

class NoSuchOpt(SynErr):pass
class NoSuchDir(SynErr):pass
class NoSuchDyn(SynErr):pass
Expand Down Expand Up @@ -60,6 +74,8 @@ class BadMesgResp(Exception):pass
class BadPropValu(SynErr):pass
class BadPySource(Exception):pass

class TeleClientSide(SynErr):pass

class HitStormLimit(SynErr):pass

class DupOpt(Exception):pass
Expand Down
4 changes: 1 addition & 3 deletions synapse/lib/mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def addSynMixin(subsys, name, cname=None):
Example:
s_mixins.addSynMixin('telepath','synapse.axon.AxonMixin')
s_mixins.addSynMixin('foo','synapse.foo.FooMixin')
'''
if cname == None:
Expand All @@ -37,5 +37,3 @@ def getSynMixins(subsys,name):
if not names:
return ()
return [ s_dyndeps.getDynLocal(name) for name in names ]

#addTeleMixin('synapse.axon.AxonMixin')
25 changes: 25 additions & 0 deletions synapse/lib/reflect.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,31 @@ def getClsNames(item):
mro = [ c for c in mro if c not in clsskip ]
return [ '%s.%s' % (c.__module__,c.__name__) for c in mro ]

def getMethName(meth):
'''
Return a fully qualified string for the <mod>.<class>.<func> name
of a given method.
'''
item = meth.__self__
mname = item.__module__
cname = item.__class__.__name__
fname = meth.__func__.__name__
return '.'.join((mname,cname,fname))

def getItemLocals(item):
'''
Iterate the locals of an item and yield (name,valu) pairs.
Example:
for name,valu in getItemLocals(item):
dostuff()
'''
for name in dir(item):
valu = getattr(item,name,None)
yield name,valu

def getItemInfo(item):
'''
Get "reflection info" dict for the given object.
Expand Down

0 comments on commit b971223

Please sign in to comment.