1
0

tty.py 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. # dockerpty: tty.py
  2. #
  3. # Copyright 2014 Chris Corbyn <[email protected]>
  4. #
  5. # Licensed under the Apache License, Version 2.0 (the "License");
  6. # you may not use this file except in compliance with the License.
  7. # You may obtain a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an "AS IS" BASIS,
  13. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. # See the License for the specific language governing permissions and
  15. # limitations under the License.
  16. from __future__ import absolute_import
  17. import os
  18. import termios
  19. import tty
  20. import fcntl
  21. import struct
  22. def size(fd):
  23. """
  24. Return a tuple (rows,cols) representing the size of the TTY `fd`.
  25. The provided file descriptor should be the stdout stream of the TTY.
  26. If the TTY size cannot be determined, returns None.
  27. """
  28. if not os.isatty(fd.fileno()):
  29. return None
  30. try:
  31. dims = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, 'hhhh'))
  32. except:
  33. try:
  34. dims = (os.environ['LINES'], os.environ['COLUMNS'])
  35. except:
  36. return None
  37. return dims
  38. class Terminal(object):
  39. """
  40. Terminal provides wrapper functionality to temporarily make the tty raw.
  41. This is useful when streaming data from a pseudo-terminal into the tty.
  42. Example:
  43. with Terminal(sys.stdin, raw=True):
  44. do_things_in_raw_mode()
  45. """
  46. def __init__(self, fd, raw=True):
  47. """
  48. Initialize a terminal for the tty with stdin attached to `fd`.
  49. Initializing the Terminal has no immediate side effects. The `start()`
  50. method must be invoked, or `with raw_terminal:` used before the
  51. terminal is affected.
  52. """
  53. self.fd = fd
  54. self.raw = raw
  55. self.original_attributes = None
  56. def __enter__(self):
  57. """
  58. Invoked when a `with` block is first entered.
  59. """
  60. self.start()
  61. return self
  62. def __exit__(self, *_):
  63. """
  64. Invoked when a `with` block is finished.
  65. """
  66. self.stop()
  67. def israw(self):
  68. """
  69. Returns True if the TTY should operate in raw mode.
  70. """
  71. return self.raw
  72. def start(self):
  73. """
  74. Saves the current terminal attributes and makes the tty raw.
  75. This method returns None immediately.
  76. """
  77. if os.isatty(self.fd.fileno()) and self.israw():
  78. self.original_attributes = termios.tcgetattr(self.fd)
  79. tty.setraw(self.fd)
  80. def stop(self):
  81. """
  82. Restores the terminal attributes back to before setting raw mode.
  83. If the raw terminal was not started, does nothing.
  84. """
  85. if self.original_attributes is not None:
  86. termios.tcsetattr(
  87. self.fd,
  88. termios.TCSADRAIN,
  89. self.original_attributes,
  90. )
  91. def __repr__(self):
  92. return "{cls}({fd}, raw={raw})".format(
  93. cls=type(self).__name__,
  94. fd=self.fd,
  95. raw=self.raw)