Funktionierender Prototyp des Serious Games zur Vermittlung von Wissen zu Software-Engineering-Arbeitsmodellen.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

__main__.py 4.6KB

1 year ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. from __future__ import annotations
  2. import argparse
  3. import os
  4. import signal
  5. import sys
  6. import threading
  7. try:
  8. import readline # noqa: F401
  9. except ImportError: # Windows has no `readline` normally
  10. pass
  11. from .sync.client import ClientConnection, connect
  12. from .version import version as websockets_version
  13. if sys.platform == "win32":
  14. def win_enable_vt100() -> None:
  15. """
  16. Enable VT-100 for console output on Windows.
  17. See also https://bugs.python.org/issue29059.
  18. """
  19. import ctypes
  20. STD_OUTPUT_HANDLE = ctypes.c_uint(-11)
  21. INVALID_HANDLE_VALUE = ctypes.c_uint(-1)
  22. ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x004
  23. handle = ctypes.windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)
  24. if handle == INVALID_HANDLE_VALUE:
  25. raise RuntimeError("unable to obtain stdout handle")
  26. cur_mode = ctypes.c_uint()
  27. if ctypes.windll.kernel32.GetConsoleMode(handle, ctypes.byref(cur_mode)) == 0:
  28. raise RuntimeError("unable to query current console mode")
  29. # ctypes ints lack support for the required bit-OR operation.
  30. # Temporarily convert to Py int, do the OR and convert back.
  31. py_int_mode = int.from_bytes(cur_mode, sys.byteorder)
  32. new_mode = ctypes.c_uint(py_int_mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING)
  33. if ctypes.windll.kernel32.SetConsoleMode(handle, new_mode) == 0:
  34. raise RuntimeError("unable to set console mode")
  35. def print_during_input(string: str) -> None:
  36. sys.stdout.write(
  37. # Save cursor position
  38. "\N{ESC}7"
  39. # Add a new line
  40. "\N{LINE FEED}"
  41. # Move cursor up
  42. "\N{ESC}[A"
  43. # Insert blank line, scroll last line down
  44. "\N{ESC}[L"
  45. # Print string in the inserted blank line
  46. f"{string}\N{LINE FEED}"
  47. # Restore cursor position
  48. "\N{ESC}8"
  49. # Move cursor down
  50. "\N{ESC}[B"
  51. )
  52. sys.stdout.flush()
  53. def print_over_input(string: str) -> None:
  54. sys.stdout.write(
  55. # Move cursor to beginning of line
  56. "\N{CARRIAGE RETURN}"
  57. # Delete current line
  58. "\N{ESC}[K"
  59. # Print string
  60. f"{string}\N{LINE FEED}"
  61. )
  62. sys.stdout.flush()
  63. def print_incoming_messages(websocket: ClientConnection, stop: threading.Event) -> None:
  64. for message in websocket:
  65. if isinstance(message, str):
  66. print_during_input("< " + message)
  67. else:
  68. print_during_input("< (binary) " + message.hex())
  69. if not stop.is_set():
  70. # When the server closes the connection, raise KeyboardInterrupt
  71. # in the main thread to exit the program.
  72. if sys.platform == "win32":
  73. ctrl_c = signal.CTRL_C_EVENT
  74. else:
  75. ctrl_c = signal.SIGINT
  76. os.kill(os.getpid(), ctrl_c)
  77. def main() -> None:
  78. # Parse command line arguments.
  79. parser = argparse.ArgumentParser(
  80. prog="python -m websockets",
  81. description="Interactive WebSocket client.",
  82. add_help=False,
  83. )
  84. group = parser.add_mutually_exclusive_group()
  85. group.add_argument("--version", action="store_true")
  86. group.add_argument("uri", metavar="<uri>", nargs="?")
  87. args = parser.parse_args()
  88. if args.version:
  89. print(f"websockets {websockets_version}")
  90. return
  91. if args.uri is None:
  92. parser.error("the following arguments are required: <uri>")
  93. # If we're on Windows, enable VT100 terminal support.
  94. if sys.platform == "win32":
  95. try:
  96. win_enable_vt100()
  97. except RuntimeError as exc:
  98. sys.stderr.write(
  99. f"Unable to set terminal to VT100 mode. This is only "
  100. f"supported since Win10 anniversary update. Expect "
  101. f"weird symbols on the terminal.\nError: {exc}\n"
  102. )
  103. sys.stderr.flush()
  104. try:
  105. websocket = connect(args.uri)
  106. except Exception as exc:
  107. print(f"Failed to connect to {args.uri}: {exc}.")
  108. sys.exit(1)
  109. else:
  110. print(f"Connected to {args.uri}.")
  111. stop = threading.Event()
  112. # Start the thread that reads messages from the connection.
  113. thread = threading.Thread(target=print_incoming_messages, args=(websocket, stop))
  114. thread.start()
  115. # Read from stdin in the main thread in order to receive signals.
  116. try:
  117. while True:
  118. # Since there's no size limit, put_nowait is identical to put.
  119. message = input("> ")
  120. websocket.send(message)
  121. except (KeyboardInterrupt, EOFError): # ^C, ^D
  122. stop.set()
  123. websocket.close()
  124. print_over_input("Connection closed.")
  125. thread.join()
  126. if __name__ == "__main__":
  127. main()