Source code for python_utils.terminal
import contextlib
import os
import typing
from . import converters
Dimensions = typing.Tuple[int, int]
OptionalDimensions = typing.Optional[Dimensions]
[docs]
def get_terminal_size() -> Dimensions: # pragma: no cover
'''Get the current size of your terminal
Multiple returns are not always a good idea, but in this case it greatly
simplifies the code so I believe it's justified. It's not the prettiest
function but that's never really possible with cross-platform code.
Returns:
width, height: Two integers containing width and height
'''
w: typing.Optional[int]
h: typing.Optional[int]
with contextlib.suppress(Exception):
# Default to 79 characters for IPython notebooks
from IPython import get_ipython # type: ignore
ipython = get_ipython()
from ipykernel import zmqshell # type: ignore
if isinstance(ipython, zmqshell.ZMQInteractiveShell):
return 79, 24
with contextlib.suppress(Exception):
# This works for Python 3, but not Pypy3. Probably the best method if
# it's supported so let's always try
import shutil
w, h = shutil.get_terminal_size()
if w and h:
# The off by one is needed due to progressbars in some cases, for
# safety we'll always substract it.
return w - 1, h
with contextlib.suppress(Exception):
w = converters.to_int(os.environ.get('COLUMNS'))
h = converters.to_int(os.environ.get('LINES'))
if w and h:
return w, h
with contextlib.suppress(Exception):
import blessings # type: ignore
terminal = blessings.Terminal()
w = terminal.width
h = terminal.height
if w and h:
return w, h
with contextlib.suppress(Exception):
# The method can return None so we don't unpack it
wh = _get_terminal_size_linux()
if wh is not None and all(wh):
return wh
with contextlib.suppress(Exception):
# Windows detection doesn't always work, let's try anyhow
wh = _get_terminal_size_windows()
if wh is not None and all(wh):
return wh
with contextlib.suppress(Exception):
# needed for window's python in cygwin's xterm!
wh = _get_terminal_size_tput()
if wh is not None and all(wh):
return wh
return 79, 24
def _get_terminal_size_windows() -> OptionalDimensions: # pragma: no cover
res = None
try:
from ctypes import windll, create_string_buffer # type: ignore
# stdin handle is -10
# stdout handle is -11
# stderr handle is -12
h = windll.kernel32.GetStdHandle(-12)
csbi = create_string_buffer(22)
res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi)
except Exception:
return None
if res:
import struct
(_, _, _, _, _, left, top, right, bottom, _, _) = struct.unpack(
"hhhhHhhhhhh", csbi.raw
)
w = right - left
h = bottom - top
return w, h
else:
return None
def _get_terminal_size_tput() -> OptionalDimensions: # pragma: no cover
# get terminal width src: http://stackoverflow.com/questions/263890/
try:
import subprocess
proc = subprocess.Popen(
['tput', 'cols'],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
output = proc.communicate(input=None)
w = int(output[0])
proc = subprocess.Popen(
['tput', 'lines'],
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
output = proc.communicate(input=None)
h = int(output[0])
return w, h
except Exception:
return None
def _get_terminal_size_linux() -> OptionalDimensions: # pragma: no cover
def ioctl_GWINSZ(fd):
try:
import fcntl
import termios
import struct
return struct.unpack(
'hh',
fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234'), # type: ignore
)
except Exception:
return None
size = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2)
if not size:
with contextlib.suppress(Exception):
fd = os.open(os.ctermid(), os.O_RDONLY) # type: ignore
size = ioctl_GWINSZ(fd)
os.close(fd)
if not size:
try:
size = os.environ['LINES'], os.environ['COLUMNS']
except Exception:
return None
return int(size[1]), int(size[0])