echoserver.py 7.4 KB

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