{"id":714,"library":"terminado","title":"Terminado","description":"Terminado is a Tornado websocket backend for the Xterm.js Javascript terminal emulator library. It enables web applications to embed interactive terminal sessions by exposing a shell (like bash or cmd.exe) over WebSockets. The library is currently at version 0.18.1 and receives regular maintenance and upkeep releases, typically focusing on dependency updates and minor bug fixes.","status":"active","version":"0.18.1","language":"python","source_language":"en","source_url":"https://github.com/jupyter/terminado","tags":["jupyter","terminal","websocket","xterm.js","tornado","pty"],"install":[{"cmd":"pip install terminado tornado ptyprocess","lang":"bash","label":"Install with core dependencies"}],"dependencies":[{"reason":"Core asynchronous networking library and web framework for the websocket backend.","package":"tornado","optional":false},{"reason":"Handles interaction with subprocesses in a pseudoterminal, essential for running shell commands.","package":"ptyprocess","optional":false},{"reason":"Alternative to ptyprocess for Windows systems to interact with pseudoterminals. Automatically chosen by terminado.","package":"pywinpty","optional":true},{"reason":"Provides XStatic resources (like XStatic-term.js) for the frontend, often used in complete deployments.","package":"tornado_xstatic","optional":true}],"imports":[{"note":"TermSocket is found within the 'websocket' submodule, not directly under 'terminado'.","wrong":"from terminado import TermSocket","symbol":"TermSocket","correct":"from terminado.websocket import TermSocket"},{"note":"Terminal managers are located in the 'management' submodule.","wrong":"from terminado import SingleTermManager","symbol":"SingleTermManager","correct":"from terminado.management import SingleTermManager"},{"symbol":"UniqueTermManager","correct":"from terminado.management import UniqueTermManager"},{"symbol":"NamedTermManager","correct":"from terminado.management import NamedTermManager"}],"quickstart":{"code":"import os.path\nimport tornado.web\nimport tornado.ioloop\nfrom terminado.websocket import TermSocket\nfrom terminado.management import SingleTermManager\n\n# NOTE: For a full web demo with a client-side terminal, you would also need\n# tornado_xstatic and XStatic-term.js, and a corresponding HTML template.\n# This example focuses on the Python server-side setup.\n\nclass MainHandler(tornado.web.RequestHandler):\n    def get(self):\n        self.write(\"Hello from Terminado server!\\n\")\n        self.write(\"To use, connect a WebSocket-based terminal client (e.g., Xterm.js) to ws://localhost:8765/websocket\")\n\nif __name__ == \"__main__\":\n    # Use 'bash' for Linux/macOS or 'cmd.exe' for Windows\n    shell_command = os.environ.get('TERMINADO_SHELL', 'bash' if os.name != 'nt' else 'cmd.exe')\n    term_manager = SingleTermManager(shell_command=[shell_command])\n\n    handlers = [\n        (r\"/websocket\", TermSocket, {\"term_manager\": term_manager}),\n        (r\"/\", MainHandler),\n    ]\n\n    app = tornado.web.Application(handlers)\n    port = int(os.environ.get('PORT', 8765))\n    host = os.environ.get('HOST', 'localhost') # Listen on localhost for security\n    app.listen(port, host)\n    print(f\"Terminado server started on http://{host}:{port}\")\n    print(f\"WebSocket endpoint at ws://{host}:{port}/websocket\")\n    print(f\"Using shell: {shell_command}\")\n    print(\"Press Ctrl+C to stop.\")\n    try:\n        tornado.ioloop.IOLoop.current().start()\n    except KeyboardInterrupt:\n        print(\"\\nServer stopped.\")\n    finally:\n        term_manager.shutdown()","lang":"python","description":"This quickstart sets up a basic Tornado web application with a `TermSocket` handler. It creates a single terminal instance managed by `SingleTermManager` and exposes it over a WebSocket endpoint. A simple HTTP handler is also included for initial access. Note that a client-side JavaScript terminal emulator (like Xterm.js) is required to interact with this backend. The `TERMINADO_SHELL`, `PORT`, and `HOST` environment variables can be used to customize the shell, port, and host respectively."},"warnings":[{"fix":"Upgrade Python to 3.8 or higher. If unable to upgrade Python, pin `terminado` to `<0.18.0`.","message":"Python 3.7 support was removed in version 0.18.0. Users on Python 3.7 or older must upgrade their Python environment or use an older `terminado` version (<=0.17.x).","severity":"breaking","affected_versions":">=0.18.0"},{"fix":"Upgrade Python to 3.7 or higher. If unable to upgrade Python, pin `terminado` to `<0.13.0`.","message":"Python 3.6 support was officially dropped in version 0.13.0. Users on Python 3.6 must upgrade their Python environment or use an older `terminado` version (<=0.12.x).","severity":"breaking","affected_versions":">=0.13.0"},{"fix":"Subclass `terminado.TermSocket` and override the `get` method to integrate with your application's authentication system (e.g., as shown in Jupyter's `IPythonHandler` example). Ensure only authenticated and authorized users can access the websocket endpoint. Listen only on `localhost` unless explicitly needed and secured.","message":"The `TermSocket` handler does not implement any authentication or authorization by default. Exposing it directly (especially with a shell command) without proper security measures allows anyone who can connect to run arbitrary commands on the server. This is a significant security risk.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Integrate a JavaScript frontend library (e.g., Xterm.js) into your web application and configure it to connect to the `TermSocket` WebSocket endpoint provided by Terminado.","message":"Terminado is a *backend* library. It provides the server-side Python component for a terminal, but requires a client-side JavaScript terminal emulator (such as Xterm.js) to provide a complete interactive terminal experience in a web browser. The Python library does not include a GUI itself.","severity":"gotcha","affected_versions":"All versions"},{"fix":"Upgrade `terminado` to version 0.16.0 or newer to benefit from the fix for this issue. Ensure your application handles large inputs gracefully.","message":"Older versions of Terminado (prior to 0.16.0) had a bug where large stdin writes could cause the Tornado server to hang, leading to unresponsive terminals.","severity":"gotcha","affected_versions":"<0.16.0"},{"fix":"Investigate `terminado`'s compatibility with Python 3.13. This might require updating `terminado` to a version explicitly supporting Python 3.13 or pinning to an older Python version (e.g., 3.12) if a compatible `terminado` version is not available.","message":"Tests for `terminado` are timing out when run with Python 3.13. This may indicate a new compatibility issue specific to Python 3.13 that causes the library to hang or perform unacceptably slowly, leading to test failures.","severity":"breaking","affected_versions":"All versions"}],"env_vars":null,"last_verified":"2026-05-12T18:13:50.374Z","next_check":"2026-06-26T00:00:00.000Z","problems":[{"fix":"Install the 'terminado' library using pip: `pip install terminado`","cause":"The 'terminado' package has not been installed in the Python environment where the code is being executed.","error":"ModuleNotFoundError: No module named 'terminado'"},{"fix":"Ensure the 'TerminalWebsocket' handler is properly added to your 'tornado.web.Application' routes with the correct path, for example:\n```python\nfrom tornado.web import Application\nfrom terminado import TerminalWebsocket\n\ndef make_app():\n    return Application([\n        (r\"/terminals/websocket/(.*)\", TerminalWebsocket, {'shell_command': ['bash']}),\n    ])\n```","cause":"The Tornado web application is not correctly configured to route WebSocket connections to the 'terminado.TerminalWebsocket' handler, often due to an incorrect URL pattern or handler registration.","error":"WebSocket connection to 'ws://localhost:8888/terminals/websocket/1' failed: HTTP error: status 404"},{"fix":"Ensure the class name is spelled correctly and imported directly from the top-level 'terminado' package (as of version 0.18.1):\n```python\nfrom terminado import TerminalWebsocket, TermManager\n```","cause":"The class name 'TerminalWebsocket' (or 'TermManager') is misspelled, or an attempt is made to import it from an incorrect submodule path, or the 'terminado' version is incompatible with the import statement.","error":"ImportError: cannot import name 'TerminalWebsocket' from 'terminado'"},{"fix":"Verify the 'shell_command' points to a valid and executable shell on your system. Use an absolute path if the command is not in the system's PATH, for example:\n```python\n# For Linux/macOS\nshell_command=['/bin/bash']\n# For Windows\nshell_command=['cmd.exe']\n# Or for PowerShell on Windows\n# shell_command=['powershell.exe']\n```","cause":"The 'shell_command' configured for the 'terminado' handler refers to an executable that does not exist at the specified path, is not in the system's PATH, or lacks execution permissions on the server.","error":"bash: command not found (or 'Permission denied' in the terminal output in browser)"}],"ecosystem":"pypi","meta_description":null,"install_score":100,"install_tag":"verified","quickstart_score":0,"quickstart_tag":"stale","pypi_latest":"0.18.1","install_checks":{"last_tested":"2026-05-12","tag":"verified","tag_description":"installs cleanly on critical runtimes, fast import, recently tested","results":[{"runtime":"python:3.10-alpine","python_version":"3.10","os_libc":"alpine (musl)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":0.32,"mem_mb":9.3,"disk_size":"21.4M"},{"runtime":"python:3.10-alpine","python_version":"3.10","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.33,"mem_mb":9.3,"disk_size":"21.4M"},{"runtime":"python:3.10-slim","python_version":"3.10","os_libc":"slim (glibc)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":1.8,"import_time_s":0.26,"mem_mb":9.3,"disk_size":"22M"},{"runtime":"python:3.10-slim","python_version":"3.10","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.28,"mem_mb":9.3,"disk_size":"22M"},{"runtime":"python:3.11-alpine","python_version":"3.11","os_libc":"alpine (musl)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":0.44,"mem_mb":10.6,"disk_size":"24.2M"},{"runtime":"python:3.11-alpine","python_version":"3.11","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.48,"mem_mb":10.6,"disk_size":"24.2M"},{"runtime":"python:3.11-slim","python_version":"3.11","os_libc":"slim (glibc)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":1.9,"import_time_s":0.39,"mem_mb":10.6,"disk_size":"25M"},{"runtime":"python:3.11-slim","python_version":"3.11","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.37,"mem_mb":10.6,"disk_size":"25M"},{"runtime":"python:3.12-alpine","python_version":"3.12","os_libc":"alpine (musl)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":0.61,"mem_mb":10.6,"disk_size":"15.8M"},{"runtime":"python:3.12-alpine","python_version":"3.12","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.66,"mem_mb":10.6,"disk_size":"15.8M"},{"runtime":"python:3.12-slim","python_version":"3.12","os_libc":"slim (glibc)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":1.8,"import_time_s":0.63,"mem_mb":10.6,"disk_size":"16M"},{"runtime":"python:3.12-slim","python_version":"3.12","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.69,"mem_mb":10.6,"disk_size":"16M"},{"runtime":"python:3.13-alpine","python_version":"3.13","os_libc":"alpine (musl)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":0.66,"mem_mb":10.9,"disk_size":"15.5M"},{"runtime":"python:3.13-alpine","python_version":"3.13","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.65,"mem_mb":10.9,"disk_size":"15.4M"},{"runtime":"python:3.13-slim","python_version":"3.13","os_libc":"slim (glibc)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":1.9,"import_time_s":0.57,"mem_mb":10.9,"disk_size":"16M"},{"runtime":"python:3.13-slim","python_version":"3.13","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.66,"mem_mb":10.9,"disk_size":"16M"},{"runtime":"python:3.9-alpine","python_version":"3.9","os_libc":"alpine (musl)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":null,"import_time_s":0.29,"mem_mb":9.2,"disk_size":"20.9M"},{"runtime":"python:3.9-alpine","python_version":"3.9","os_libc":"alpine (musl)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.35,"mem_mb":9.2,"disk_size":"20.9M"},{"runtime":"python:3.9-slim","python_version":"3.9","os_libc":"slim (glibc)","variant":" $EXIT -eq 0 ","exit_code":0,"wheel_type":"wheel","failure_reason":null,"install_time_s":2.2,"import_time_s":0.25,"mem_mb":9.2,"disk_size":"21M"},{"runtime":"python:3.9-slim","python_version":"3.9","os_libc":"slim (glibc)","variant":"default","exit_code":0,"wheel_type":null,"failure_reason":null,"install_time_s":null,"import_time_s":0.25,"mem_mb":9.2,"disk_size":"21M"}]},"quickstart_checks":{"last_tested":"2026-04-24","tag":"stale","tag_description":"widespread failures or data too old to trust","results":[{"runtime":"python:3.10-alpine","exit_code":-1},{"runtime":"python:3.10-slim","exit_code":-1},{"runtime":"python:3.11-alpine","exit_code":-1},{"runtime":"python:3.11-slim","exit_code":-1},{"runtime":"python:3.12-alpine","exit_code":-1},{"runtime":"python:3.12-slim","exit_code":-1},{"runtime":"python:3.13-alpine","exit_code":-1},{"runtime":"python:3.13-slim","exit_code":-1},{"runtime":"python:3.9-alpine","exit_code":-1},{"runtime":"python:3.9-slim","exit_code":-1}]}}