PageRenderTime 32ms CodeModel.GetById 20ms RepoModel.GetById 1ms app.codeStats 0ms

/python3/tcpclient.py

https://github.com/deavid/irpc
Python | 235 lines | 231 code | 3 blank | 1 comment | 4 complexity | 678a07996716b9a8c2d1e3808042d740 MD5 | raw file
  1. #!/usr/local/bin/python3
  2. import socket
  3. import threading
  4. import time
  5. import json
  6. import irpcchatter
  7. class ExecuteRemoteCommand:
  8. def __init__(self,chatter, command, local_args = [], local_kwargs = {}, args = [], kwargs = {}, autoremove_id = True):
  9. self.chatter = chatter
  10. self.prepared = False
  11. self.started = False
  12. self.executed = False
  13. self.returnValue = None
  14. self.queuedAnswer = None
  15. self.autoremove_id = autoremove_id
  16. self.command = command
  17. self.local_args = local_args
  18. self.local_kwargs = local_kwargs
  19. self.args = args
  20. self.kwargs = kwargs
  21. self.trama = ""
  22. def getUnqueuedRandID(self):
  23. if self.autoremove_id:
  24. def key():
  25. import random
  26. i = random.randint(0,255)
  27. return "x%X" % i
  28. else:
  29. def key():
  30. import random
  31. i = random.randint(0,256*256-1)
  32. return "%s%X" % (self.command[:2],i)
  33. k = key()
  34. while k in self.chatter.language.cmds.answer.answerqueue: k = key()
  35. return k
  36. def prepare(self, id = "auto"):
  37. # Complete all stuff here and set the bytes to send
  38. if self.prepared: return
  39. if id == "auto":
  40. id = self.getUnqueuedRandID()
  41. if id is None:
  42. idobj = ""
  43. else:
  44. idobj = "@" + id
  45. self.queuedAnswer = self.chatter.language.cmds.answer.queueAnswerFor(id, self.autoremove_id)
  46. trama_args = [
  47. "!%s%s" % (self.command,idobj),
  48. ]
  49. for arg in self.local_args:
  50. tr1 = ":" + arg
  51. trama_args.append(tr1)
  52. for k,arg in self.local_kwargs.items():
  53. tr1 = k + ":" + arg
  54. trama_args.append(tr1)
  55. for arg in self.args:
  56. val = json.dumps(arg)
  57. tr1 = "=" + val
  58. trama_args.append(tr1)
  59. for k,arg in self.kwargs.items():
  60. val = json.dumps(arg)
  61. tr1 = k + "=" + val
  62. trama_args.append(tr1)
  63. self.trama = "\t".join(trama_args) + "\n"
  64. self.prepared = True
  65. def start(self):
  66. if not self.prepared: self.prepare()
  67. self.chatter.push(self.trama.encode("utf8"))
  68. self.started = True
  69. def getReturnValue(self, timeout = 10):
  70. if not self.started: self.start()
  71. if self.executed:
  72. return self.returnValue
  73. if not self.queuedAnswer.wait(timeout):
  74. print("timeout!!!")
  75. return None
  76. self.returnValue = self.processAnswer(self.queuedAnswer)
  77. self.executed = True
  78. return self.returnValue
  79. def processAnswer(self,answer):
  80. if answer.type == "":
  81. return answer.value
  82. if answer.type == "Exception":
  83. raise NameError(answer.value)
  84. class RemoteIRPC:
  85. def __init__(self,host, port):
  86. self.addr = host
  87. self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  88. self.socket.connect((host,port))
  89. self.socket.setblocking(0)
  90. self.lang = irpcchatter.BaseLanguageSpec()
  91. self.local_address = self.socket.getsockname()
  92. self.remote_address = self.socket.getpeername()
  93. # print("Conected to ", self.remote_address) # debug
  94. self.chatter = irpcchatter.BaseChatter(sock = self.socket, addr = self.addr)
  95. self.chatter.setup(self.lang) # Configura y da de alta todo el lenguaje
  96. self.thread = threading.Thread(target=self.chatter.loop)
  97. self.thread.setDaemon(True)
  98. self.thread.start()
  99. self.timeout = 30
  100. self.monitored_events = {}
  101. def call(self, fn, *args,getReturnValue = True, **kwargs):
  102. #ret = self.execute("call",local_kwargs={"fn":fn},args = args,kwargs = kwargs)
  103. #print(ret.type)
  104. #print(ret.value)
  105. exe = ExecuteRemoteCommand(self.chatter, "call", local_kwargs={"fn":fn},args = args,kwargs = kwargs)
  106. exe.start()
  107. if getReturnValue:
  108. ret = exe.getReturnValue()
  109. return ret
  110. else:
  111. return exe
  112. def monitor(self, ev, *args, **kwargs):
  113. if ev in self.monitored_events:
  114. raise NameError("Error: tried to monitor event '%s' twice." % ev)
  115. exe = ExecuteRemoteCommand(self.chatter, "monitor", local_kwargs={"ev":ev},args = args,kwargs = kwargs, autoremove_id = False)
  116. exe.prepare()
  117. exe.queuedAnswer.event_name = ev
  118. exe.queuedAnswer.callback = self.monitor_callback
  119. exe.queuedAnswer.connected_functions = []
  120. exe.start()
  121. self.monitored_events[ev] = exe
  122. def monitor_callback(self, answer):
  123. if answer.type != "Signal": return
  124. if hasattr(answer,"connected_functions"):
  125. try:
  126. akwargs = dict(answer.value)
  127. for fn, fargs, fkwargs in answer.connected_functions:
  128. kwargs = dict(list(akwargs.items()) + list(fkwargs.items()))
  129. try:
  130. fn(*fargs,**kwargs)
  131. except:
  132. print("Error ocurred when calling connected functions for event:")
  133. print(traceback.format_exc())
  134. except:
  135. print("Error ocurred when calling connected functions for event:")
  136. print(traceback.format_exc())
  137. #print("Received signal for event %s: %s" % (answer.event_name,answer.value))
  138. def connect(self, ev, fn, *args, **kwargs):
  139. """
  140. Connects the event 'ev' to the function 'fn'.
  141. The function fn is called every time the event is raised.
  142. The function receives the keyword arguments from the signal.
  143. """
  144. if ev not in self.monitored_events: self.monitor(ev)
  145. exe = self.monitored_events[ev]
  146. obj = (fn,args,kwargs)
  147. if obj not in exe.queuedAnswer.connected_functions:
  148. exe.queuedAnswer.connected_functions.append(obj)
  149. else:
  150. print("Warning: function connected twice to the event. Ignoring.")
  151. def disconnect(self, ev, fn, *args, **kwargs):
  152. """
  153. Disconnects the function fn from the event ev
  154. """
  155. obj = (fn,args,kwargs)
  156. if obj in exe.queuedAnswer.connected_functions:
  157. exe.queuedAnswer.connected_functions.remove(obj)
  158. else:
  159. print("Warning: function not connected to the event. Ignoring.")
  160. def testSerialized(remote,iterations):
  161. for n in range(iterations):
  162. remote.call("addItem", item=n)
  163. def testConcurrent(remote,iterations):
  164. lstExe = []
  165. for n in range(iterations):
  166. lstExe.append(remote.call("addItem", item=n, getReturnValue = False))
  167. for exe in lstExe: exe.getReturnValue()
  168. def testEvent(item):
  169. print("Event for Item:",item)
  170. def main():
  171. remote = RemoteIRPC("localhost",10123)
  172. remote.connect("testEvent",testEvent)
  173. remote.call("clearItems")
  174. #testSerialized(remote,iterations = 200)
  175. testConcurrent(remote,iterations = 10000)
  176. print(sum(remote.call("getItems")))
  177. print("done")
  178. if __name__ == "__main__":
  179. main()