File: terminal_utils.py

package info (click to toggle)
ros-osrf-pycommon 2.1.7-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 360 kB
  • sloc: python: 1,726; makefile: 146; xml: 16
file content (103 lines) | stat: -rw-r--r-- 3,516 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
# Copyright 2014 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""
This module has a miscellaneous set of functions for working with terminals.

You can use the :py:func:`get_terminal_dimensions` to get the width and
height of the terminal as a tuple.

You can also use the :py:func:`is_tty` function to determine if a given object
is a tty.
"""

import os
import struct
import subprocess

__all__ = ['GetTerminalDimensionsError', 'get_terminal_dimensions', 'is_tty']


class GetTerminalDimensionsError(Exception):
    """Raised when the terminal dimensions cannot be determined."""
    pass


def _get_terminal_dimensions_windows():
    try:
        from ctypes import create_string_buffer
        from ctypes import windll
    except ImportError as exc:
        raise GetTerminalDimensionsError("Failed to get dimensions: {0}"
                                         .format(exc))
    STDOUT = -11
    h = windll.kernel32.GetStdHandle(STDOUT)
    buffer_info = create_string_buffer(22)
    if not windll.kernel32.GetConsoleScreenBufferInfo(h, buffer_info):
        raise GetTerminalDimensionsError(
            "Call to windll.kernel32.GetConsoleScreenBufferInfo failed")

    try:
        (_, _, _, _, _, left, top, right, bottom, _, _) \
            = struct.unpack("hhhhHhhhhhh", buffer_info.raw)
    except struct.error as exc:
        raise GetTerminalDimensionsError("Failed to unpack data: {0}"
                                         .format(exc))
    width = right - left + 1
    height = bottom - top + 1

    return width, height


def _get_terminal_dimensions_unix():
    # This function uses `tput` and should work on any Unix system
    # See: http://en.wikipedia.org/wiki/Tput
    try:
        width = subprocess.check_output(['tput', 'cols'])

        width = int(width.strip())
    except (subprocess.CalledProcessError, ValueError) as exc:
        raise GetTerminalDimensionsError("Failed to get width: {0}"
                                         .format(exc))
    try:
        height = subprocess.check_output(['tput', 'lines'])

        height = int(height.strip())
    except (subprocess.CalledProcessError, ValueError) as exc:
        raise GetTerminalDimensionsError("Failed to get height: {0}"
                                         .format(exc))
    return width, height


def get_terminal_dimensions():
    """Returns the width and height of the terminal.

    :returns: width and height in that order as a tuple
    :rtype: tuple
    :raises: GetTerminalDimensionsError when the terminal dimensions
        cannot be determined
    """
    if os.name in ['nt']:
        return _get_terminal_dimensions_windows()
    return _get_terminal_dimensions_unix()


def is_tty(stream):
    """Returns True if the given stream is a tty, else False

    :param stream: object to be checked for being a tty
    :returns: True if the given object is a tty, otherwise False
    :rtype: bool
    """
    return hasattr(stream, 'isatty') and stream.isatty()