Fixes terminal_width on Windows

Windows does not support fcntl and the changes introduced
by commit e7c3c62275
broke Windows support.

This patch adds Windows support by means of the appropriate
OS API.

Closes-Bug: #1543958
Change-Id: Icd251b340457e44b0560b8fbb131dd3cf13fcedd
This commit is contained in:
Alessandro Pilotti 2016-02-10 12:42:08 +02:00 committed by Doug Hellmann
parent 31adfbc2d0
commit f8f9069cb9
2 changed files with 61 additions and 3 deletions

View File

@ -31,7 +31,7 @@ def test_utils_terminal_width_get_terminal_size(mock_os):
assert width is None
@mock.patch('cliff.utils.ioctl')
@mock.patch('fcntl.ioctl')
def test_utils_terminal_width_ioctl(mock_ioctl):
if hasattr(os, 'get_terminal_size'):
raise nose.SkipTest('only needed for python 3.2 and before')
@ -42,3 +42,26 @@ def test_utils_terminal_width_ioctl(mock_ioctl):
mock_ioctl.side_effect = IOError()
width = utils.terminal_width(sys.stdout)
assert width is None
@mock.patch('cliff.utils.ctypes')
@mock.patch('sys.platform', 'win32')
def test_utils_terminal_width_windows(mock_ctypes):
if hasattr(os, 'get_terminal_size'):
raise nose.SkipTest('only needed for python 3.2 and before')
mock_ctypes.create_string_buffer.return_value.raw = struct.pack(
'hhhhHhhhhhh', 101, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
mock_ctypes.windll.kernel32.GetStdHandle.return_value = -11
mock_ctypes.windll.kernel32.GetConsoleScreenBufferInfo.return_value = 1
width = utils.terminal_width(sys.stdout)
assert width == 101
mock_ctypes.windll.kernel32.GetConsoleScreenBufferInfo.return_value = 0
width = utils.terminal_width(sys.stdout)
assert width is None
width = utils.terminal_width('foo')
assert width is None

View File

@ -11,10 +11,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
from fcntl import ioctl
import ctypes
import os
import struct
import termios
import sys
# Each edit operation is assigned different cost, such as:
# 'w' means swap operation, the cost is 0;
@ -101,6 +101,41 @@ def terminal_width(stdout):
except OSError:
return None
if sys.platform == 'win32':
return _get_terminal_width_windows(stdout)
else:
return _get_terminal_width_ioctl(stdout)
def _get_terminal_width_windows(stdout):
STD_INPUT_HANDLE = -10
STD_OUTPUT_HANDLE = -11
STD_ERROR_HANDLE = -12
std_to_win_handle = {
sys.stdin: STD_INPUT_HANDLE,
sys.stdout: STD_OUTPUT_HANDLE,
sys.stderr: STD_ERROR_HANDLE}
std_handle = std_to_win_handle.get(stdout)
if not std_handle:
return None
handle = ctypes.windll.kernel32.GetStdHandle(std_handle)
csbi = ctypes.create_string_buffer(22)
res = ctypes.windll.kernel32.GetConsoleScreenBufferInfo(handle, csbi)
if res:
(size_x, size_y, cur_pos_x, cur_pos_y, attr,
left, top, right, bottom, max_size_x, max_size_y) = struct.unpack(
"hhhhHhhhhhh", csbi.raw)
return size_x
def _get_terminal_width_ioctl(stdout):
from fcntl import ioctl
import termios
try:
# winsize structure has 4 unsigned short fields
winsize = b'\0' * struct.calcsize('hhhh')