mirror of
https://github.com/kennethreitz/clint.git
synced 2026-06-05 06:46:16 +00:00
Merge branch 'feature/widths'
This commit is contained in:
+1
-1
@@ -1 +1 @@
|
||||
include HISTORY.rst README.rst LICENSE AUTHORS NOTICE
|
||||
include HISTORY.rst README.rst LICENSE AUTHORS NOTICE
|
||||
+33
-14
@@ -25,19 +25,19 @@ __all__ = ('Args', )
|
||||
|
||||
|
||||
def _expand_path(path):
|
||||
"""Expands directories and globs in given path."""
|
||||
"""Expands directories and globs in given path."""
|
||||
|
||||
paths = []
|
||||
paths = []
|
||||
|
||||
if os.path.isdir(path):
|
||||
if os.path.isdir(path):
|
||||
|
||||
for (dir, dirs, files) in os.walk(path):
|
||||
for file in files:
|
||||
paths.append(os.path.join(dir, file))
|
||||
else:
|
||||
paths.extend(glob(path))
|
||||
for (dir, dirs, files) in os.walk(path):
|
||||
for file in files:
|
||||
paths.append(os.path.join(dir, file))
|
||||
else:
|
||||
paths.extend(glob(path))
|
||||
|
||||
return paths
|
||||
return paths
|
||||
|
||||
|
||||
class Args(object):
|
||||
@@ -69,7 +69,7 @@ class Args(object):
|
||||
|
||||
|
||||
def __contains__(self, x):
|
||||
return bool(self.first(x))
|
||||
return self.first(x) is not None
|
||||
|
||||
|
||||
def get(self, x):
|
||||
@@ -90,7 +90,7 @@ class Args(object):
|
||||
|
||||
def _remove(x):
|
||||
found = self.first(x)
|
||||
if found:
|
||||
if found is not None:
|
||||
self._args.pop(found)
|
||||
|
||||
if is_collection(x):
|
||||
@@ -133,7 +133,7 @@ class Args(object):
|
||||
if is_collection(x):
|
||||
for item in x:
|
||||
found = _find(item)
|
||||
if found:
|
||||
if found is not None:
|
||||
return found
|
||||
return None
|
||||
else:
|
||||
@@ -181,6 +181,25 @@ class Args(object):
|
||||
else:
|
||||
return _find(x)
|
||||
|
||||
|
||||
def start_with(self, x):
|
||||
"""Returns all arguments beginning with given string (or list thereof)"""
|
||||
|
||||
_args = []
|
||||
|
||||
for arg in self.all:
|
||||
if is_collection(x):
|
||||
for _x in x:
|
||||
if arg.startswith(x):
|
||||
_args.append(arg)
|
||||
break
|
||||
else:
|
||||
if arg.startswith(x):
|
||||
_args.append(arg)
|
||||
|
||||
return Args(_args, no_argv=True)
|
||||
|
||||
|
||||
def contains_at(self, x, index):
|
||||
"""Tests if given [list of] string is at given index."""
|
||||
|
||||
@@ -305,7 +324,7 @@ class Args(object):
|
||||
def flags(self):
|
||||
"""Returns Arg object including only flagged arguments."""
|
||||
|
||||
return self.all_with('-')
|
||||
return self.start_with('-')
|
||||
|
||||
|
||||
@property
|
||||
@@ -343,7 +362,7 @@ class Args(object):
|
||||
if not os.path.exists(arg):
|
||||
_args.append(arg)
|
||||
|
||||
return _args
|
||||
return Args(_args, no_argv=True)
|
||||
|
||||
@property
|
||||
def copy(self):
|
||||
|
||||
+41
-9
@@ -11,16 +11,21 @@ This module provides a simple and elegant wrapper for colorama.
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import re
|
||||
import sys
|
||||
|
||||
from ..packages import colorama
|
||||
|
||||
DISABLE_COLOR = False
|
||||
|
||||
|
||||
colorama.init(autoreset=True)
|
||||
if sys.stdout.isatty():
|
||||
colorama.init(autoreset=True)
|
||||
|
||||
|
||||
__all__ = (
|
||||
'red', 'green', 'yellow', 'blue',
|
||||
'black', 'magenta', 'cyan', 'white'
|
||||
'black', 'magenta', 'cyan', 'white',
|
||||
'clean', 'disable'
|
||||
)
|
||||
|
||||
|
||||
@@ -30,9 +35,16 @@ class ColoredString(object):
|
||||
super(ColoredString, self).__init__()
|
||||
self.s = s
|
||||
self.color = color
|
||||
self.color_str = '%s%s%s' % (
|
||||
getattr(colorama.Fore, self.color), self.s, colorama.Fore.RESET)
|
||||
|
||||
|
||||
@property
|
||||
def color_str(self):
|
||||
if sys.stdout.isatty() and not DISABLE_COLOR:
|
||||
return '%s%s%s' % (
|
||||
getattr(colorama.Fore, self.color), self.s, colorama.Fore.RESET)
|
||||
else:
|
||||
return self.s
|
||||
|
||||
|
||||
def __len__(self):
|
||||
return len(self.s)
|
||||
|
||||
@@ -46,17 +58,31 @@ class ColoredString(object):
|
||||
return self.color_str
|
||||
|
||||
def __add__(self, other):
|
||||
return str(self.color_str) + str(other)
|
||||
self.s += other
|
||||
return self
|
||||
|
||||
def __radd__(self, other):
|
||||
return str(other) + str(self.color_str)
|
||||
self.s = other + self.s
|
||||
return self
|
||||
|
||||
def __mul__(self, other):
|
||||
return (self.color_str * other)
|
||||
|
||||
def split(self, x=' '):
|
||||
return self.color_str.split(x)
|
||||
return map(self._new, self.s.split(x))
|
||||
|
||||
def _new(self, s):
|
||||
return ColoredString(self.color, s)
|
||||
|
||||
|
||||
def clean(s):
|
||||
strip = re.compile("([^-_a-zA-Z0-9!@#%&=,/'\";:~`\$\^\*\(\)\+\[\]\.\{\}\|\?\<\>\\]+|[^\s]+)")
|
||||
txt = strip.sub('', str(s))
|
||||
|
||||
strip = re.compile(r'\[\d+m')
|
||||
txt = strip.sub('', txt)
|
||||
|
||||
return txt
|
||||
|
||||
|
||||
def black(string):
|
||||
@@ -82,3 +108,9 @@ def cyan(string):
|
||||
|
||||
def white(string):
|
||||
return ColoredString('WHITE', string)
|
||||
|
||||
def disable():
|
||||
"""Disables colors."""
|
||||
global DISABLE_COLOR
|
||||
|
||||
DISABLE_COLOR = True
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
clint.textui.columns
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Core TextUI functionality for column formatting.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from .formatters import max_width, min_width
|
||||
from ..utils import tsplit
|
||||
|
||||
import sys
|
||||
|
||||
|
||||
NEWLINES = ('\n', '\r', '\r\n')
|
||||
|
||||
|
||||
|
||||
def _find_unix_console_width():
|
||||
import termios, fcntl, struct, sys
|
||||
|
||||
# fcntl.ioctl will fail if stdout is not a tty
|
||||
if not sys.stdout.isatty():
|
||||
return None
|
||||
|
||||
s = struct.pack("HHHH", 0, 0, 0, 0)
|
||||
fd_stdout = sys.stdout.fileno()
|
||||
size = fcntl.ioctl(fd_stdout, termios.TIOCGWINSZ, s)
|
||||
height, width = struct.unpack("HHHH", size)[:2]
|
||||
return width
|
||||
|
||||
|
||||
def _find_windows_console_width():
|
||||
# http://code.activestate.com/recipes/440694/
|
||||
from ctypes import windll, create_string_buffer
|
||||
STDIN, STDOUT, STDERR = -10, -11, -12
|
||||
|
||||
h = windll.kernel32.GetStdHandle(STDERR)
|
||||
csbi = create_string_buffer(22)
|
||||
res = windll.kernel32.GetConsoleScreenBufferInfo(h, csbi)
|
||||
|
||||
if res:
|
||||
import struct
|
||||
(bufx, bufy, curx, cury, wattr,
|
||||
left, top, right, bottom,
|
||||
maxx, maxy) = struct.unpack("hhhhHhhhhhh", csbi.raw)
|
||||
sizex = right - left + 1
|
||||
sizey = bottom - top + 1
|
||||
return sizex
|
||||
|
||||
|
||||
def console_width(kwargs):
|
||||
""""Determine console_width."""
|
||||
|
||||
if sys.platform.startswith('win'):
|
||||
console_width = _find_windows_console_width()
|
||||
else:
|
||||
console_width = _find_unix_console_width()
|
||||
|
||||
_width = kwargs.get('width', None)
|
||||
if _width:
|
||||
console_width = _width
|
||||
else:
|
||||
if not console_width:
|
||||
console_width = 80
|
||||
|
||||
return console_width
|
||||
|
||||
|
||||
|
||||
def columns(*cols, **kwargs):
|
||||
|
||||
columns = list(cols)
|
||||
|
||||
cwidth = console_width(kwargs)
|
||||
|
||||
_big_col = None
|
||||
_total_cols = 0
|
||||
|
||||
|
||||
for i, (string, width) in enumerate(cols):
|
||||
|
||||
if width is not None:
|
||||
_total_cols += (width + 1)
|
||||
cols[i][0] = max_width(string, width).split('\n')
|
||||
else:
|
||||
_big_col = i
|
||||
|
||||
if _big_col:
|
||||
cols[_big_col][1] = (cwidth - _total_cols) - len(cols)
|
||||
cols[_big_col][0] = max_width(cols[_big_col][0], cols[_big_col][1]).split('\n')
|
||||
|
||||
height = len(max([c[0] for c in cols], key=len))
|
||||
|
||||
for i, (strings, width) in enumerate(cols):
|
||||
|
||||
for _ in range(height - len(strings)):
|
||||
cols[i][0].append('')
|
||||
|
||||
for j, string in enumerate(strings):
|
||||
cols[i][0][j] = min_width(string, width)
|
||||
|
||||
stack = [c[0] for c in cols]
|
||||
_out = []
|
||||
|
||||
for i in range(height):
|
||||
_row = ''
|
||||
|
||||
for col in stack:
|
||||
_row += col[i]
|
||||
_row += ' '
|
||||
|
||||
_out.append(_row)
|
||||
# try:
|
||||
# pass
|
||||
# except:
|
||||
# pass
|
||||
|
||||
|
||||
|
||||
|
||||
return '\n'.join(_out)
|
||||
|
||||
|
||||
# string = max_width(string, width)
|
||||
# string = min_width(string, width)
|
||||
# pass
|
||||
# columns.append()
|
||||
|
||||
|
||||
|
||||
###########################
|
||||
|
||||
a = 'this is text that goes into a small column\n cool?'
|
||||
b = 'this is other text\nothertext\nothertext'
|
||||
|
||||
#columns((a, 10), (b, 20), (b, None))
|
||||
@@ -14,15 +14,19 @@ from __future__ import absolute_import
|
||||
import sys
|
||||
|
||||
from .progress import progressbar
|
||||
from .formatters import max_width, min_width
|
||||
from ..utils import tsplit
|
||||
|
||||
|
||||
__all__ = ('puts', 'puts_err', 'indent', 'progressbar')
|
||||
__all__ = ('puts', 'puts_err', 'indent', 'progressbar', 'max_width', 'min_width')
|
||||
|
||||
|
||||
STDOUT = sys.stdout.write
|
||||
STDERR = sys.stderr.write
|
||||
|
||||
NEWLINES = ('\n', '\r', '\r\n')
|
||||
|
||||
|
||||
|
||||
class Writer(object):
|
||||
"""WriterUtilized by context managers."""
|
||||
@@ -60,10 +64,11 @@ class Writer(object):
|
||||
def __call__(self, s, newline=True, stream=STDOUT):
|
||||
|
||||
if newline:
|
||||
s = tsplit(s, ('\n', '\r', '\r\n'))
|
||||
s = tsplit(s, NEWLINES)
|
||||
s = map(str, s)
|
||||
indent = ''.join(self.shared['indent_strings'])
|
||||
|
||||
s = ('\n' + indent).join(s)
|
||||
s = (str('\n' + indent)).join(s)
|
||||
|
||||
_str = ''.join((
|
||||
''.join(self.shared['indent_strings']),
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
"""
|
||||
clint.textui.formatters
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Core TextUI functionality for text formatting.
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
from .colored import ColoredString, clean
|
||||
from ..utils import tsplit, schunk
|
||||
|
||||
|
||||
NEWLINES = ('\n', '\r', '\r\n')
|
||||
|
||||
|
||||
def min_width(string, cols, padding=' '):
|
||||
"""Returns given string with right padding."""
|
||||
|
||||
is_color = isinstance(string, ColoredString)
|
||||
|
||||
stack = tsplit(str(string), NEWLINES)
|
||||
|
||||
for i, substring in enumerate(stack):
|
||||
_sub = clean(substring).ljust((cols + 0), padding)
|
||||
if is_color:
|
||||
_sub = (_sub.replace(clean(substring), substring))
|
||||
stack[i] = _sub
|
||||
|
||||
return '\n'.join(stack)
|
||||
|
||||
|
||||
def max_width(string, cols, separator='\n'):
|
||||
"""Returns a freshly formatted """
|
||||
|
||||
is_color = isinstance(string, ColoredString)
|
||||
|
||||
if is_color:
|
||||
offset = 10
|
||||
string_copy = string._new('')
|
||||
else:
|
||||
offset = 0
|
||||
|
||||
stack = tsplit(string, NEWLINES)
|
||||
|
||||
for i, substring in enumerate(stack):
|
||||
stack[i] = substring.split()
|
||||
|
||||
_stack = []
|
||||
|
||||
for row in stack:
|
||||
_row = ['',]
|
||||
_row_i = 0
|
||||
|
||||
for word in row:
|
||||
if (len(_row[_row_i]) + len(word)) < (cols + offset):
|
||||
_row[_row_i] += word
|
||||
_row[_row_i] += ' '
|
||||
|
||||
elif len(word) > (cols - offset):
|
||||
|
||||
# ensure empty row
|
||||
if len(_row[_row_i]):
|
||||
_row.append('')
|
||||
_row_i += 1
|
||||
|
||||
chunks = schunk(word, (cols + offset))
|
||||
for i, chunk in enumerate(chunks):
|
||||
if not (i + 1) == len(chunks):
|
||||
_row[_row_i] += chunk
|
||||
_row.append('')
|
||||
_row_i += 1
|
||||
else:
|
||||
_row[_row_i] += chunk
|
||||
_row[_row_i] += ' '
|
||||
else:
|
||||
_row.append('')
|
||||
_row_i += 1
|
||||
_row[_row_i] += word
|
||||
_row[_row_i] += ' '
|
||||
|
||||
_row = map(str, _row)
|
||||
_stack.append(separator.join(_row))
|
||||
|
||||
_s = '\n'.join(_stack)
|
||||
if is_color:
|
||||
_s = string_copy._new(_s)
|
||||
return _s
|
||||
@@ -49,3 +49,24 @@ def tsplit(string, delimiters):
|
||||
|
||||
return stack
|
||||
|
||||
def schunk(string, size):
|
||||
"""Splits string into n sized chunks."""
|
||||
|
||||
stack = []
|
||||
|
||||
substack = []
|
||||
current_count = 0
|
||||
|
||||
for char in string:
|
||||
if not current_count < size:
|
||||
stack.append(''.join(substack))
|
||||
substack = []
|
||||
current_count = 0
|
||||
|
||||
substack.append(char)
|
||||
current_count += 1
|
||||
|
||||
if len(substack):
|
||||
stack.append(''.join(substack))
|
||||
|
||||
return stack
|
||||
|
||||
Executable
+23
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
sys.path.insert(0, os.path.abspath('..'))
|
||||
|
||||
from clint.textui import puts, colored
|
||||
from clint.textui.columns import columns
|
||||
|
||||
lorem = 'Lorem ipsum dolor sit amet, consehdfhdfhdfhdfhdfhctetur adi pisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'
|
||||
|
||||
if __name__ == '__main__':
|
||||
# puts(min_width('test\nit', 20) + ' me')
|
||||
# puts(max_width(lorem, 20) + ' me')
|
||||
|
||||
# print max_width(lorem, 45)
|
||||
|
||||
col = 60
|
||||
|
||||
puts(columns([(colored.red('Column 1')), col], [(colored.green('Column Two')), None], [(colored.magenta('Column III')), col]))
|
||||
puts(columns(['hi there my name is kenneth and this is a columns', col], [lorem, None], ['kenneths', col]))
|
||||
+1
-3
@@ -6,7 +6,7 @@
|
||||
import unittest
|
||||
|
||||
|
||||
class TablibTestCase(unittest.TestCas):
|
||||
class TablibTestCase(unittest.TestCase):
|
||||
"""Tablib test cases."""
|
||||
|
||||
def setUp(self):
|
||||
@@ -16,7 +16,5 @@ class TablibTestCase(unittest.TestCas):
|
||||
def tearDown(self):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user