echoserver.py 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. """
  2. Echo server for test purposes.
  3. This is usually invoked by starting this module as a script:
  4. :command:`python -m Pyro4.test.echoserver`
  5. or simply: :command:`pyro4-test-echoserver`
  6. It is also possible to use the :class:`EchoServer` in user code
  7. but that is not terribly useful.
  8. Pyro - Python Remote Objects. Copyright by Irmen de Jong (irmen@razorvine.net).
  9. """
  10. import sys
  11. import time
  12. from optparse import OptionParser
  13. from Pyro4 import threadutil
  14. from Pyro4 import naming
  15. import Pyro4
  16. __all__ = ["EchoServer"]
  17. @Pyro4.expose
  18. class EchoServer(object):
  19. """
  20. The echo server object that is provided as a Pyro object by this module.
  21. If its :attr:`verbose` attribute is set to ``True``, it will print messages as it receives calls.
  22. """
  23. _verbose = False
  24. _must_shutdown = False
  25. def echo(self, message):
  26. """return the message"""
  27. if self._verbose:
  28. message_str = repr(message).encode(sys.stdout.encoding, errors="replace").decode(sys.stdout.encoding)
  29. print("%s - echo: %s" % (time.asctime(), message_str))
  30. return message
  31. def error(self):
  32. """generates a simple exception without text"""
  33. if self._verbose:
  34. print("%s - error: generating exception" % time.asctime())
  35. raise ValueError()
  36. def error_with_text(self):
  37. """generates a simple exception with message"""
  38. if self._verbose:
  39. print("%s - error: generating exception" % time.asctime())
  40. raise ValueError("the message of the error")
  41. @Pyro4.oneway
  42. def oneway_echo(self, message):
  43. """just like echo, but oneway; the client won't wait for response"""
  44. if self._verbose:
  45. message_str = repr(message).encode(sys.stdout.encoding, errors="replace").decode(sys.stdout.encoding)
  46. print("%s - oneway_echo: %s" % (time.asctime(), message_str))
  47. return "bogus return value"
  48. def slow(self):
  49. """returns (and prints) a message after a certain delay"""
  50. if self._verbose:
  51. print("%s - slow: waiting a bit..." % time.asctime())
  52. time.sleep(5)
  53. if self._verbose:
  54. print("%s - slow: returning result" % time.asctime())
  55. return "Finally, an answer!"
  56. def generator(self):
  57. """a generator function that returns some elements on demand"""
  58. yield "one"
  59. yield "two"
  60. yield "three"
  61. @Pyro4.oneway
  62. def oneway_slow(self):
  63. """prints a message after a certain delay, and returns; but the client won't wait for it"""
  64. if self._verbose:
  65. print("%s - oneway_slow: waiting a bit..." % time.asctime())
  66. time.sleep(5)
  67. if self._verbose:
  68. print("%s - oneway_slow: returning result" % time.asctime())
  69. return "bogus return value"
  70. def _private(self):
  71. """a 'private' method that should not be accessible"""
  72. return "should not be allowed"
  73. def __private(self):
  74. """another 'private' method that should not be accessible"""
  75. return "should not be allowed"
  76. def __dunder__(self):
  77. """a double underscore method that should be accessible normally"""
  78. return "should be allowed (dunder)"
  79. def shutdown(self):
  80. """called to signal the echo server to shut down"""
  81. if self._verbose:
  82. print("%s - shutting down" % time.asctime())
  83. self._must_shutdown = True
  84. @property
  85. def verbose(self):
  86. return self._verbose
  87. @verbose.setter
  88. def verbose(self, onoff):
  89. self._verbose = bool(onoff)
  90. class NameServer(threadutil.Thread):
  91. def __init__(self, hostname, hmac=None):
  92. super(NameServer, self).__init__()
  93. self.setDaemon(1)
  94. self.hostname = hostname
  95. self.hmac = hmac
  96. self.started = threadutil.Event()
  97. def run(self):
  98. self.uri, self.ns_daemon, self.bc_server = naming.startNS(self.hostname, hmac=self.hmac)
  99. self.started.set()
  100. if self.bc_server:
  101. self.bc_server.runInThread()
  102. self.ns_daemon.requestLoop()
  103. def startNameServer(host, hmac=None):
  104. ns = NameServer(host, hmac=hmac)
  105. ns.start()
  106. ns.started.wait()
  107. return ns
  108. def main(args=None, returnWithoutLooping=False):
  109. parser = OptionParser()
  110. parser.add_option("-H", "--host", default="localhost", help="hostname to bind server on (default=%default)")
  111. parser.add_option("-p", "--port", type="int", default=0, help="port to bind server on")
  112. parser.add_option("-u", "--unixsocket", help="Unix domain socket name to bind server on")
  113. parser.add_option("-n", "--naming", action="store_true", default=False, help="register with nameserver")
  114. parser.add_option("-N", "--nameserver", action="store_true", default=False, help="also start a nameserver")
  115. parser.add_option("-v", "--verbose", action="store_true", default=False, help="verbose output")
  116. parser.add_option("-q", "--quiet", action="store_true", default=False, help="don't output anything")
  117. parser.add_option("-k", "--key", help="the HMAC key to use")
  118. options, args = parser.parse_args(args)
  119. if options.verbose:
  120. options.quiet = False
  121. if not options.quiet:
  122. print("Starting Pyro's built-in test echo server.")
  123. Pyro4.config.SERVERTYPE = "multiplex"
  124. hmac = (options.key or "").encode("utf-8")
  125. if not hmac and not options.quiet:
  126. print("Warning: HMAC key not set. Anyone can connect to this server!")
  127. nameserver = None
  128. if options.nameserver:
  129. options.naming = True
  130. nameserver = startNameServer(options.host, hmac=hmac)
  131. d = Pyro4.Daemon(host=options.host, port=options.port, unixsocket=options.unixsocket)
  132. if hmac:
  133. d._pyroHmacKey = hmac
  134. echo = EchoServer()
  135. echo._verbose = options.verbose
  136. objectName = "test.echoserver"
  137. uri = d.register(echo, objectName)
  138. if options.naming:
  139. host, port = None, None
  140. if nameserver is not None:
  141. host, port = nameserver.uri.host, nameserver.uri.port
  142. ns = naming.locateNS(host, port, hmac_key=hmac)
  143. ns.register(objectName, uri)
  144. if options.verbose:
  145. print("using name server at %s" % ns._pyroUri)
  146. if nameserver is not None:
  147. if nameserver.bc_server:
  148. print("broadcast server running at %s" % nameserver.bc_server.locationStr)
  149. else:
  150. print("not using a broadcast server")
  151. else:
  152. if options.verbose:
  153. print("not using a name server.")
  154. if not options.quiet:
  155. print("object name: %s" % objectName)
  156. print("echo uri: %s" % uri)
  157. print("echoserver running.")
  158. if returnWithoutLooping:
  159. return d, echo, uri # for unit testing
  160. else:
  161. d.requestLoop(loopCondition=lambda: not echo._must_shutdown)
  162. d.close()
  163. if __name__ == "__main__":
  164. main()