Terminal.__init__()   A
last analyzed

Complexity

Conditions 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 3
dl 0
loc 6
rs 10
c 0
b 0
f 0
cc 1
nop 1
1
import os
2
import platform
3
import shlex
4
import struct
5
import subprocess
6
from typing import Optional, Tuple
7
8
9
class Terminal(object):
10
    """
11
    Represents the current terminal.
12
    """
13
14
    # ------------------------------------------------------------------------------------------------------------------
15
    def __init__(self):
16
        """
17
        Object constructor.
18
        """
19
        self._width = None
20
        self._height = None
21
22
    # ------------------------------------------------------------------------------------------------------------------
23
    @property
24
    def width(self) -> Optional[int]:
25
        """
26
        Returns the width of the terminal.
27
        """
28
        width = os.getenv('COLUMNS', '').strip()
29
        if width:
30
            return int(width)
31
32
        if self._width is None:
33
            self._init_dimensions()
34
35
        return self._width
36
37
    # ------------------------------------------------------------------------------------------------------------------
38
    @property
39
    def height(self) -> Optional[int]:
40
        """
41
        Returns the height of the terminal.
42
        """
43
        height = os.getenv('LINES', '').strip()
44
        if height:
45
            return int(height)
46
47
        if self._height is None:
48
            self._init_dimensions()
49
50
        return self._height
51
52
    # ------------------------------------------------------------------------------------------------------------------
53
    def _init_dimensions(self) -> None:
54
        """
55
        Initializes the terminal dimensions.
56
        """
57
        current_os = platform.system().lower()
58
        dimensions = None
59
60
        if current_os.lower() == 'windows':
61
            dimensions = self._get_terminal_size_windows()
62
            if dimensions is None:
63
                dimensions = self._get_terminal_size_tput()
64
        elif current_os.lower() in ['linux', 'darwin'] or current_os.startswith('cygwin'):
65
            dimensions = self._get_terminal_size_linux()
66
67
        if dimensions is None:
68
            dimensions = 80, 25
69
70
        self._width, self._height = dimensions
71
72
    # ------------------------------------------------------------------------------------------------------------------
73
    @staticmethod
74
    def _get_terminal_size_windows() -> Optional[Tuple[int, int]]:
75
        """
76
        Returns the width and height of the terminal on MS-Windows
77
        """
78
        try:
79
            from ctypes import windll, create_string_buffer
80
            # stdin handle is -10
81
            # stdout handle is -11
82
            # stderr handle is -12
83
            h = windll.kernel32.GetStdHandle(-12)
84
            csbi = create_string_buffer(22)
85
            res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi)
86
            if res:
87
                (bufx, bufy, curx, cury, wattr, left, top, right, bottom, maxx, maxy) = struct.unpack("hhhhHhhhhhh",
88
                                                                                                      csbi.raw)
89
                sizex = right - left + 1
90
                sizey = bottom - top + 1
91
                return sizex, sizey
92
        except:
93
            return None
94
95
    # ------------------------------------------------------------------------------------------------------------------
96
    @staticmethod
97
    def _get_terminal_size_tput() -> Optional[Tuple[int, int]]:
98
        """
99
        Returns the width and height of the terminal on MS-Windows
100
101
        See: https://stackoverflow.com/questions/263890/how-do-i-find-the-width-height-of-a-terminal-window
102
        """
103
        try:
104
            cols = int(subprocess.check_output(shlex.split('tput cols'), stderr=subprocess.STDOUT))
105
            rows = int(subprocess.check_output(shlex.split('tput lines'), stderr=subprocess.STDOUT))
106
107
            return cols, rows
108
        except:
109
            return None
110
111
    # ------------------------------------------------------------------------------------------------------------------
112
    def _get_terminal_size_linux(self) -> Optional[Tuple[int, int]]:
113
        """
114
        Returns the width and height of the terminal on Linux.
115
        """
116
117
        def ioctl_GWINSZ(fd):
118
            try:
119
                import fcntl
120
                import termios
121
                cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234'))
122
                return cr
123
            except:
124
                pass
125
126
        cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2)
127
        if not cr:
128
            try:
129
                fd = os.open(os.ctermid(), os.O_RDONLY)
130
                cr = ioctl_GWINSZ(fd)
131
                os.close(fd)
132
            except:
133
                pass
134
135
        if not cr:
136
            try:
137
                cr = (os.environ['LINES'], os.environ['COLUMNS'])
138
            except:
139
                return None
140
141
        return int(cr[1]), int(cr[0])
142
143
# ----------------------------------------------------------------------------------------------------------------------
144