mirror of
https://github.com/kennethreitz/clint.git
synced 2026-06-05 23:00:18 +00:00
Compare commits
52 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9d3693d644 | |||
| ee695135a7 | |||
| e4ba837bad | |||
| 3b77ef3b43 | |||
| 2698af12dd | |||
| 195ab0de4d | |||
| b16b2248cd | |||
| dc8fbfd355 | |||
| 8f42b40900 | |||
| 96013432d9 | |||
| 84ce2ed9b1 | |||
| 8c3cbe984b | |||
| d0fe6df171 | |||
| 7865d7a375 | |||
| 0931dbff9f | |||
| 8dc046b9c7 | |||
| 39322ea11f | |||
| a190f64e3c | |||
| a7a28da410 | |||
| a1203ff9d9 | |||
| 16e50a62e1 | |||
| a173f763d0 | |||
| e101c56eed | |||
| 52ff92d7d5 | |||
| 34ec848479 | |||
| 2bd5aef5bc | |||
| a47ed4f711 | |||
| d0a5237afe | |||
| d6cb52f8b3 | |||
| fa79d0d4ea | |||
| a09b1efa76 | |||
| 140d85898d | |||
| 93d10a8acb | |||
| 9392b77dcd | |||
| 1162b46f0b | |||
| 82753f5172 | |||
| 714d06ecfa | |||
| c391eae023 | |||
| 1c1f1c93f9 | |||
| f06e164fc6 | |||
| a226b3f2dc | |||
| e3f283ed6e | |||
| 6a417e5bdf | |||
| 7431c17fe4 | |||
| 85d5f52549 | |||
| 7be8140d91 | |||
| fb96edf52f | |||
| 439de410c0 | |||
| 5a88ee3a52 | |||
| 3077b0f53e | |||
| 5e7a191957 | |||
| 6aedd380cf |
+36
-10
@@ -1,11 +1,37 @@
|
|||||||
.idea
|
*.py[cod]
|
||||||
.project
|
|
||||||
.settings
|
|
||||||
.pydevproject
|
|
||||||
MANIFEST
|
|
||||||
*.pyc
|
|
||||||
.tox
|
|
||||||
build/
|
|
||||||
clint.egg-info/
|
|
||||||
dist/
|
|
||||||
|
|
||||||
|
# Packages
|
||||||
|
*.egg
|
||||||
|
*.egg-info
|
||||||
|
dist
|
||||||
|
build
|
||||||
|
eggs
|
||||||
|
parts
|
||||||
|
bin
|
||||||
|
var
|
||||||
|
sdist
|
||||||
|
develop-eggs
|
||||||
|
.installed.cfg
|
||||||
|
lib
|
||||||
|
lib64
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
.coverage
|
||||||
|
.tox
|
||||||
|
nosetests.xml
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
|
||||||
|
# Mr Developer
|
||||||
|
.mr.developer.cfg
|
||||||
|
.project
|
||||||
|
.pydevproject
|
||||||
|
|
||||||
|
*.DS_Store
|
||||||
|
*.dylib
|
||||||
|
.idea/
|
||||||
|
docs/_*
|
||||||
|
|||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
language: python
|
||||||
|
python:
|
||||||
|
- 2.6
|
||||||
|
- 2.7
|
||||||
|
- 3.2
|
||||||
|
- 3.3
|
||||||
|
- pypy
|
||||||
|
install:
|
||||||
|
- pip install pytest args coveralls pytest-cov
|
||||||
|
script:
|
||||||
|
- py.test --cov clint
|
||||||
|
after_success:
|
||||||
|
- coveralls
|
||||||
@@ -31,3 +31,4 @@ Patches and Suggestions
|
|||||||
- Eric Anderson
|
- Eric Anderson
|
||||||
- Joshua Richardson
|
- Joshua Richardson
|
||||||
- Brandon Liu <thenovices>
|
- Brandon Liu <thenovices>
|
||||||
|
- Kentaro Wada <www.kentaro.wada@gmail.com>
|
||||||
|
|||||||
+23
-2
@@ -1,6 +1,20 @@
|
|||||||
History
|
History
|
||||||
-------
|
-------
|
||||||
|
|
||||||
|
0.5.1
|
||||||
|
+++++
|
||||||
|
* Fix line width calculation in max_width when using coloured text (thanks to @wkentaro)
|
||||||
|
|
||||||
|
0.5.0
|
||||||
|
+++++
|
||||||
|
* Added option prompt
|
||||||
|
|
||||||
|
|
||||||
|
0.4.1
|
||||||
|
+++++
|
||||||
|
* Fix bug in logic that decides whether progress bars should be hidden or not
|
||||||
|
|
||||||
|
|
||||||
0.4.0
|
0.4.0
|
||||||
+++++
|
+++++
|
||||||
* clint.textui.prompt now has a query function with validators! (thanks to @aeby) - see `examples/prompt.py`
|
* clint.textui.prompt now has a query function with validators! (thanks to @aeby) - see `examples/prompt.py`
|
||||||
@@ -12,39 +26,47 @@ History
|
|||||||
+++++
|
+++++
|
||||||
* Clint now obeys the CLINT_FORCE_COLOR environmental variable
|
* Clint now obeys the CLINT_FORCE_COLOR environmental variable
|
||||||
|
|
||||||
|
|
||||||
0.3.6
|
0.3.6
|
||||||
+++++
|
+++++
|
||||||
* Fixed faulty PyPI deployment
|
* Fixed faulty PyPI deployment
|
||||||
|
|
||||||
|
|
||||||
0.3.5
|
0.3.5
|
||||||
+++++
|
+++++
|
||||||
* progress.bar is now a context manager - doesn't require an iterable anymore (thanks to @jric)
|
* progress.bar is now a context manager - doesn't require an iterable anymore (thanks to @jric)
|
||||||
* Bug fixes
|
* Bug fixes
|
||||||
|
|
||||||
|
|
||||||
0.3.4
|
0.3.4
|
||||||
+++++
|
+++++
|
||||||
* Fixed Python 3 basestring deprecation
|
* Fixed Python 3 basestring deprecation
|
||||||
* Fixed examples
|
* Fixed examples
|
||||||
|
|
||||||
|
|
||||||
0.3.3
|
0.3.3
|
||||||
+++++
|
+++++
|
||||||
* Fixed Python 3 build issues
|
* Fixed Python 3 build issues
|
||||||
* Fixed README and HISTORY being installed to /usr
|
* Fixed README and HISTORY being installed to /usr
|
||||||
* Support added for bold text
|
* Support added for bold text
|
||||||
|
|
||||||
|
|
||||||
0.3.2
|
0.3.2
|
||||||
+++++
|
+++++
|
||||||
* Unknown
|
* Unknown
|
||||||
|
|
||||||
|
|
||||||
0.3.1
|
0.3.1
|
||||||
+++++
|
+++++
|
||||||
* Unknown
|
* Unknown
|
||||||
|
|
||||||
|
|
||||||
0.3.0
|
0.3.0
|
||||||
+++++
|
+++++
|
||||||
|
|
||||||
* Python 3 support!
|
* Python 3 support!
|
||||||
|
|
||||||
|
|
||||||
0.2.4
|
0.2.4
|
||||||
+++++
|
+++++
|
||||||
|
|
||||||
@@ -55,7 +77,7 @@ History
|
|||||||
0.2.3
|
0.2.3
|
||||||
+++++
|
+++++
|
||||||
|
|
||||||
* Only init colors if they are used (iPython compatability)
|
* Only init colors if they are used (iPython compatibility)
|
||||||
* New progress module
|
* New progress module
|
||||||
* Various bugfixes
|
* Various bugfixes
|
||||||
|
|
||||||
@@ -102,7 +124,6 @@ History
|
|||||||
* Lots of Examples
|
* Lots of Examples
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
0.1.0 (2011-03-20)
|
0.1.0 (2011-03-20)
|
||||||
++++++++++++++++++
|
++++++++++++++++++
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
ISC License
|
||||||
|
|
||||||
Copyright (c) 2011, Kenneth Reitz <me@kennethreitz.com>
|
Copyright (c) 2011, Kenneth Reitz <me@kennethreitz.com>
|
||||||
|
|
||||||
Permission to use, copy, modify, and/or distribute this software for any
|
Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
|||||||
+4
-3
@@ -1,4 +1,4 @@
|
|||||||
Clint: Python Command-line Application Tools
|
Clint: Python Command-line Interface Tools
|
||||||
============================================
|
============================================
|
||||||
|
|
||||||
**Clint** is a module filled with a set of awesome tools for developing
|
**Clint** is a module filled with a set of awesome tools for developing
|
||||||
@@ -28,7 +28,8 @@ Run the various executables in examples_ to get a good feel for what Clint offer
|
|||||||
|
|
||||||
You'll never want to not use it.
|
You'll never want to not use it.
|
||||||
|
|
||||||
|
.. image:: https://travis-ci.org/kennethreitz/clint.png?branch=master
|
||||||
|
:target: https://travis-ci.org/kennethreitz/clint
|
||||||
|
|
||||||
Current Features:
|
Current Features:
|
||||||
-----------------
|
-----------------
|
||||||
@@ -74,7 +75,7 @@ I want to quote my console text (like email). ::
|
|||||||
|
|
||||||
I want to color my console text. ::
|
I want to color my console text. ::
|
||||||
|
|
||||||
>>> from clint.textui import colored
|
>>> from clint.textui import colored, puts
|
||||||
|
|
||||||
>>> puts(colored.red('red text'))
|
>>> puts(colored.red('red text'))
|
||||||
red text
|
red text
|
||||||
|
|||||||
+2
-2
@@ -26,8 +26,8 @@ from .pipes import piped_in
|
|||||||
|
|
||||||
|
|
||||||
__title__ = 'clint'
|
__title__ = 'clint'
|
||||||
__version__ = '0.4.0'
|
__version__ = '0.5.1'
|
||||||
__build__ = 0x000400
|
__build__ = 0x000501
|
||||||
__author__ = 'Kenneth Reitz'
|
__author__ = 'Kenneth Reitz'
|
||||||
__license__ = 'ISC'
|
__license__ = 'ISC'
|
||||||
__copyright__ = 'Copyright 2012 Kenneth Reitz'
|
__copyright__ = 'Copyright 2012 Kenneth Reitz'
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import sys
|
|||||||
PY3 = sys.version_info[0] >= 3
|
PY3 = sys.version_info[0] >= 3
|
||||||
|
|
||||||
from ..packages import colorama
|
from ..packages import colorama
|
||||||
|
from ..utils import basestring
|
||||||
|
|
||||||
__all__ = (
|
__all__ = (
|
||||||
'red', 'green', 'yellow', 'blue',
|
'red', 'green', 'yellow', 'blue',
|
||||||
@@ -42,7 +43,10 @@ class ColoredString(object):
|
|||||||
"""Enhanced string for __len__ operations on Colored output."""
|
"""Enhanced string for __len__ operations on Colored output."""
|
||||||
def __init__(self, color, s, always_color=False, bold=False):
|
def __init__(self, color, s, always_color=False, bold=False):
|
||||||
super(ColoredString, self).__init__()
|
super(ColoredString, self).__init__()
|
||||||
self.s = s
|
if not PY3 and isinstance(s, unicode):
|
||||||
|
self.s = s.encode('utf-8')
|
||||||
|
else:
|
||||||
|
self.s = s
|
||||||
self.color = color
|
self.color = color
|
||||||
self.always_color = always_color
|
self.always_color = always_color
|
||||||
self.bold = bold
|
self.bold = bold
|
||||||
@@ -93,10 +97,7 @@ class ColoredString(object):
|
|||||||
__str__ = __unicode__
|
__str__ = __unicode__
|
||||||
else:
|
else:
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
value = self.color_str
|
return self.color_str
|
||||||
if isinstance(value, bytes):
|
|
||||||
return value
|
|
||||||
return value.encode('utf8')
|
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
return iter(self.color_str)
|
return iter(self.color_str)
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ import sys
|
|||||||
|
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
|
|
||||||
from .formatters import max_width, min_width
|
from .formatters import max_width, min_width, _get_max_width_context
|
||||||
from .cols import columns
|
from .cols import columns
|
||||||
from ..utils import tsplit
|
from ..utils import tsplit
|
||||||
|
|
||||||
@@ -53,6 +53,11 @@ def _indent(indent=0, quote='', indent_char=' '):
|
|||||||
|
|
||||||
def puts(s='', newline=True, stream=STDOUT):
|
def puts(s='', newline=True, stream=STDOUT):
|
||||||
"""Prints given string to stdout."""
|
"""Prints given string to stdout."""
|
||||||
|
max_width_ctx = _get_max_width_context()
|
||||||
|
if max_width_ctx:
|
||||||
|
cols, separator = max_width_ctx[-1]
|
||||||
|
s = max_width(s, cols, separator)
|
||||||
|
|
||||||
if newline:
|
if newline:
|
||||||
s = tsplit(s, NEWLINES)
|
s = tsplit(s, NEWLINES)
|
||||||
s = map(str, s)
|
s = map(str, s)
|
||||||
|
|||||||
@@ -9,12 +9,14 @@ Core TextUI functionality for text formatting.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import absolute_import
|
from __future__ import absolute_import
|
||||||
|
from contextlib import contextmanager
|
||||||
|
|
||||||
from .colored import ColoredString, clean
|
from .colored import ColoredString, clean
|
||||||
from ..utils import tsplit, schunk
|
from ..utils import tsplit, schunk
|
||||||
|
|
||||||
|
|
||||||
NEWLINES = ('\n', '\r', '\r\n')
|
NEWLINES = ('\n', '\r', '\r\n')
|
||||||
|
MAX_WIDTHS = []
|
||||||
|
|
||||||
|
|
||||||
def min_width(string, cols, padding=' '):
|
def min_width(string, cols, padding=' '):
|
||||||
@@ -33,16 +35,71 @@ def min_width(string, cols, padding=' '):
|
|||||||
return '\n'.join(stack)
|
return '\n'.join(stack)
|
||||||
|
|
||||||
|
|
||||||
def max_width(string, cols, separator='\n'):
|
def _get_max_width_context():
|
||||||
"""Returns a freshly formatted """
|
return MAX_WIDTHS
|
||||||
|
|
||||||
|
@contextmanager
|
||||||
|
def _max_width_context():
|
||||||
|
"""Max width context manager."""
|
||||||
|
try:
|
||||||
|
yield
|
||||||
|
finally:
|
||||||
|
MAX_WIDTHS.pop()
|
||||||
|
|
||||||
|
def max_width(*args, **kwargs):
|
||||||
|
"""Returns formatted text or context manager for textui:puts.
|
||||||
|
|
||||||
|
>>> from clint.textui import puts, max_width
|
||||||
|
>>> max_width('123 5678', 8)
|
||||||
|
'123 5678'
|
||||||
|
>>> max_width('123 5678', 7)
|
||||||
|
'123 \n5678'
|
||||||
|
>>> with max_width(7):
|
||||||
|
... puts('123 5678')
|
||||||
|
'123 \n5678'
|
||||||
|
"""
|
||||||
|
args = list(args)
|
||||||
|
|
||||||
|
if not args:
|
||||||
|
args.append(kwargs.get('string'))
|
||||||
|
args.append(kwargs.get('cols'))
|
||||||
|
args.append(kwargs.get('separator'))
|
||||||
|
elif len(args) == 1:
|
||||||
|
args.append(kwargs.get('cols'))
|
||||||
|
args.append(kwargs.get('separator'))
|
||||||
|
elif len(args) == 2:
|
||||||
|
args.append(kwargs.get('separator'))
|
||||||
|
|
||||||
|
string, cols, separator = args
|
||||||
|
if separator is None:
|
||||||
|
separator = '\n' # default value
|
||||||
|
if cols is None:
|
||||||
|
# cols should be specified vitally
|
||||||
|
# because string can be specified at textui:puts function
|
||||||
|
string, cols = cols, string
|
||||||
|
|
||||||
|
if string is None:
|
||||||
|
MAX_WIDTHS.append((cols, separator))
|
||||||
|
return _max_width_context()
|
||||||
|
else:
|
||||||
|
return _max_width_formatter(string, cols, separator)
|
||||||
|
|
||||||
|
|
||||||
|
def _max_width_formatter(string, cols, separator='\n'):
|
||||||
|
"""Returns a freshly formatted
|
||||||
|
:param string: string to be formatted
|
||||||
|
:type string: basestring or clint.textui.colored.ColoredString
|
||||||
|
:param cols: max width the text to be formatted
|
||||||
|
:type cols: int
|
||||||
|
:param separator: separator to break rows
|
||||||
|
:type separator: basestring
|
||||||
|
"""
|
||||||
|
|
||||||
is_color = isinstance(string, ColoredString)
|
is_color = isinstance(string, ColoredString)
|
||||||
|
|
||||||
if is_color:
|
if is_color:
|
||||||
offset = 10
|
|
||||||
string_copy = string._new('')
|
string_copy = string._new('')
|
||||||
else:
|
string = string.s
|
||||||
offset = 0
|
|
||||||
|
|
||||||
stack = tsplit(string, NEWLINES)
|
stack = tsplit(string, NEWLINES)
|
||||||
|
|
||||||
@@ -56,31 +113,36 @@ def max_width(string, cols, separator='\n'):
|
|||||||
_row_i = 0
|
_row_i = 0
|
||||||
|
|
||||||
for word in row:
|
for word in row:
|
||||||
if (len(_row[_row_i]) + len(word)) < (cols + offset):
|
if (len(_row[_row_i]) + len(word)) <= cols:
|
||||||
_row[_row_i] += word
|
_row[_row_i] += word
|
||||||
_row[_row_i] += ' '
|
_row[_row_i] += ' '
|
||||||
|
|
||||||
elif len(word) > (cols - offset):
|
elif len(word) > cols:
|
||||||
|
|
||||||
# ensure empty row
|
# ensure empty row
|
||||||
if len(_row[_row_i]):
|
if len(_row[_row_i]):
|
||||||
|
_row[_row_i] = _row[_row_i].rstrip()
|
||||||
_row.append('')
|
_row.append('')
|
||||||
_row_i += 1
|
_row_i += 1
|
||||||
|
|
||||||
chunks = schunk(word, (cols + offset))
|
chunks = schunk(word, cols)
|
||||||
for i, chunk in enumerate(chunks):
|
for i, chunk in enumerate(chunks):
|
||||||
if not (i + 1) == len(chunks):
|
if not (i + 1) == len(chunks):
|
||||||
_row[_row_i] += chunk
|
_row[_row_i] += chunk
|
||||||
|
_row[_row_i] = _row[_row_i].rstrip()
|
||||||
_row.append('')
|
_row.append('')
|
||||||
_row_i += 1
|
_row_i += 1
|
||||||
else:
|
else:
|
||||||
_row[_row_i] += chunk
|
_row[_row_i] += chunk
|
||||||
_row[_row_i] += ' '
|
_row[_row_i] += ' '
|
||||||
else:
|
else:
|
||||||
|
_row[_row_i] = _row[_row_i].rstrip()
|
||||||
_row.append('')
|
_row.append('')
|
||||||
_row_i += 1
|
_row_i += 1
|
||||||
_row[_row_i] += word
|
_row[_row_i] += word
|
||||||
_row[_row_i] += ' '
|
_row[_row_i] += ' '
|
||||||
|
else:
|
||||||
|
_row[_row_i] = _row[_row_i].rstrip()
|
||||||
|
|
||||||
_row = map(str, _row)
|
_row = map(str, _row)
|
||||||
_stack.append(separator.join(_row))
|
_stack.append(separator.join(_row))
|
||||||
|
|||||||
@@ -14,11 +14,6 @@ import sys
|
|||||||
import time
|
import time
|
||||||
|
|
||||||
STREAM = sys.stderr
|
STREAM = sys.stderr
|
||||||
# Only show bar in terminals by default (better for piping, logging etc.)
|
|
||||||
try:
|
|
||||||
HIDE_DEFAULT = not STREAM.isatty()
|
|
||||||
except AttributeError: # output does not support isatty()
|
|
||||||
HIDE_DEFAULT = True
|
|
||||||
|
|
||||||
BAR_TEMPLATE = '%s[%s%s] %i/%i - %s\r'
|
BAR_TEMPLATE = '%s[%s%s] %i/%i - %s\r'
|
||||||
MILL_TEMPLATE = '%s %s %i/%i\r'
|
MILL_TEMPLATE = '%s %s %i/%i\r'
|
||||||
@@ -48,6 +43,7 @@ class Bar(object):
|
|||||||
self.label = label
|
self.label = label
|
||||||
self.width = width
|
self.width = width
|
||||||
self.hide = hide
|
self.hide = hide
|
||||||
|
# Only show bar in terminals by default (better for piping, logging etc.)
|
||||||
if hide is None:
|
if hide is None:
|
||||||
try:
|
try:
|
||||||
self.hide = not STREAM.isatty()
|
self.hide = not STREAM.isatty()
|
||||||
@@ -107,21 +103,21 @@ class Bar(object):
|
|||||||
return time.strftime('%H:%M:%S', time.gmtime(seconds))
|
return time.strftime('%H:%M:%S', time.gmtime(seconds))
|
||||||
|
|
||||||
|
|
||||||
def bar(it, label='', width=32, hide=HIDE_DEFAULT, empty_char=BAR_EMPTY_CHAR,
|
def bar(it, label='', width=32, hide=None, empty_char=BAR_EMPTY_CHAR,
|
||||||
filled_char=BAR_FILLED_CHAR, expected_size=None, every=1):
|
filled_char=BAR_FILLED_CHAR, expected_size=None, every=1):
|
||||||
"""Progress iterator. Wrap your iterables with it."""
|
"""Progress iterator. Wrap your iterables with it."""
|
||||||
|
|
||||||
count = len(it) if expected_size is None else expected_size
|
count = len(it) if expected_size is None else expected_size
|
||||||
|
|
||||||
with Bar(label=label, width=width, hide=hide, empty_char=BAR_EMPTY_CHAR,
|
with Bar(label=label, width=width, hide=hide, empty_char=empty_char,
|
||||||
filled_char=BAR_FILLED_CHAR, expected_size=count, every=every) \
|
filled_char=filled_char, expected_size=count, every=every) \
|
||||||
as bar:
|
as bar:
|
||||||
for i, item in enumerate(it):
|
for i, item in enumerate(it):
|
||||||
yield item
|
yield item
|
||||||
bar.show(i + 1)
|
bar.show(i + 1)
|
||||||
|
|
||||||
|
|
||||||
def dots(it, label='', hide=HIDE_DEFAULT, every=1):
|
def dots(it, label='', hide=None, every=1):
|
||||||
"""Progress iterator. Prints a dot for each item being iterated"""
|
"""Progress iterator. Prints a dot for each item being iterated"""
|
||||||
|
|
||||||
count = 0
|
count = 0
|
||||||
@@ -143,7 +139,7 @@ def dots(it, label='', hide=HIDE_DEFAULT, every=1):
|
|||||||
STREAM.flush()
|
STREAM.flush()
|
||||||
|
|
||||||
|
|
||||||
def mill(it, label='', hide=HIDE_DEFAULT, expected_size=None, every=1):
|
def mill(it, label='', hide=None, expected_size=None, every=1):
|
||||||
"""Progress iterator. Prints a mill while iterating over the items."""
|
"""Progress iterator. Prints a mill while iterating over the items."""
|
||||||
|
|
||||||
def _mill_char(_i):
|
def _mill_char(_i):
|
||||||
|
|||||||
+97
-3
@@ -11,10 +11,11 @@ Module for simple interactive prompts handling
|
|||||||
from __future__ import absolute_import, print_function
|
from __future__ import absolute_import, print_function
|
||||||
|
|
||||||
from re import match, I
|
from re import match, I
|
||||||
|
import getpass
|
||||||
|
|
||||||
from .core import puts
|
from .core import puts
|
||||||
from .colored import yellow
|
from .colored import yellow
|
||||||
from .validators import RegexValidator
|
from .validators import RegexValidator, OptionValidator
|
||||||
|
|
||||||
try:
|
try:
|
||||||
raw_input
|
raw_input
|
||||||
@@ -59,7 +60,7 @@ def yn(prompt, default='y', batch=False):
|
|||||||
return True if default == 'n' else False
|
return True if default == 'n' else False
|
||||||
|
|
||||||
|
|
||||||
def query(prompt, default='', validators=None, batch=False):
|
def query(prompt, default='', validators=None, batch=False, mask_input=False):
|
||||||
# Set the nonempty validator as default
|
# Set the nonempty validator as default
|
||||||
if validators is None:
|
if validators is None:
|
||||||
validators = [RegexValidator(r'.+')]
|
validators = [RegexValidator(r'.+')]
|
||||||
@@ -76,7 +77,8 @@ def query(prompt, default='', validators=None, batch=False):
|
|||||||
# If batch option is True then auto reply
|
# If batch option is True then auto reply
|
||||||
# with default input
|
# with default input
|
||||||
if not batch:
|
if not batch:
|
||||||
user_input = raw_input(prompt).strip() or default
|
user_input_fn = getpass.getpass if mask_input else raw_input
|
||||||
|
user_input = user_input_fn(prompt).strip() or default
|
||||||
else:
|
else:
|
||||||
print(prompt)
|
print(prompt)
|
||||||
user_input = ''
|
user_input = ''
|
||||||
@@ -88,3 +90,95 @@ def query(prompt, default='', validators=None, batch=False):
|
|||||||
return user_input
|
return user_input
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
puts(yellow(e.message))
|
puts(yellow(e.message))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def options(prompt, options, default=None, batch=False):
|
||||||
|
'''
|
||||||
|
|
||||||
|
:param prompt:
|
||||||
|
:param options:
|
||||||
|
this can be either a list of strings, in which case it will be presented like:
|
||||||
|
prompt:
|
||||||
|
(1) this is the first string
|
||||||
|
(2) this is the second string
|
||||||
|
(3) this is the third string
|
||||||
|
|
||||||
|
please select 1-3:
|
||||||
|
|
||||||
|
or a list of dictionaries in the format of:
|
||||||
|
{ { 'selector' : 'this is what the user will enter to select the option'
|
||||||
|
'prompt': 'this is the string that will be displayed, this can be omitted if the selector is also a prompt',
|
||||||
|
'return': 'this is what is returned to the calling procedure, if omitted, the option selector will be used' }
|
||||||
|
|
||||||
|
so, to replicate the above, the dict could look like:
|
||||||
|
|
||||||
|
[ {'selector':1,'prompt':'this is the first string','return':1},
|
||||||
|
{'selector':2,'prompt':'this is the second string','return':2},
|
||||||
|
{'selector':3,'prompt':'this is the third string'}
|
||||||
|
|
||||||
|
:param default: should be set to the default selector (if desired)
|
||||||
|
:param batch: True/False, will auto-return the default
|
||||||
|
:return:
|
||||||
|
'''
|
||||||
|
# Build fix options and build validator
|
||||||
|
|
||||||
|
validator_list = []
|
||||||
|
return_dict = {}
|
||||||
|
|
||||||
|
if isinstance(options[0],dict):
|
||||||
|
for item in options:
|
||||||
|
item['selector'] = str(item['selector'])
|
||||||
|
item['prompt'] = str(item['prompt'])
|
||||||
|
if 'return' not in item:
|
||||||
|
item['return'] = item['selector']
|
||||||
|
validator_list.append(item['selector'])
|
||||||
|
return_dict[item['selector']] = item['return']
|
||||||
|
else:
|
||||||
|
options_strings = options
|
||||||
|
options = []
|
||||||
|
for key, opt in enumerate(options_strings):
|
||||||
|
item = {}
|
||||||
|
item['selector'] = str(key+1)
|
||||||
|
item['prompt'] = str(opt)
|
||||||
|
item['return'] = key+1
|
||||||
|
|
||||||
|
return_dict[item['selector']] = item['return']
|
||||||
|
validator_list.append(item['selector'])
|
||||||
|
options.append(item)
|
||||||
|
|
||||||
|
validators = [OptionValidator(validator_list)]
|
||||||
|
|
||||||
|
# Let's build the prompt
|
||||||
|
|
||||||
|
prompt += '\n'
|
||||||
|
|
||||||
|
# building the options list
|
||||||
|
for o in options:
|
||||||
|
prompt += '[{selector}] {prompt}\n'.format(**o)
|
||||||
|
|
||||||
|
prompt += '\n'
|
||||||
|
|
||||||
|
if default:
|
||||||
|
prompt += '[' + default + '] '
|
||||||
|
|
||||||
|
# If input is not valid keep asking
|
||||||
|
while True:
|
||||||
|
# If batch option is True then auto reply
|
||||||
|
# with default input
|
||||||
|
if not batch:
|
||||||
|
user_input = raw_input(prompt).strip() or default
|
||||||
|
else:
|
||||||
|
print(prompt)
|
||||||
|
user_input = ''
|
||||||
|
|
||||||
|
|
||||||
|
# Validate the user input
|
||||||
|
try:
|
||||||
|
for validator in validators:
|
||||||
|
user_input = validator(user_input)
|
||||||
|
# convert user input to defined return value
|
||||||
|
user_input = return_dict[user_input]
|
||||||
|
return user_input
|
||||||
|
except Exception as e:
|
||||||
|
puts(yellow(e.message))
|
||||||
|
|||||||
@@ -102,3 +102,21 @@ class IntegerValidator(object):
|
|||||||
return int(value)
|
return int(value)
|
||||||
except (TypeError, ValueError):
|
except (TypeError, ValueError):
|
||||||
raise ValidationError(self.message)
|
raise ValidationError(self.message)
|
||||||
|
|
||||||
|
class OptionValidator(object):
|
||||||
|
message = 'Select from the list of valid options.'
|
||||||
|
|
||||||
|
def __init__(self, options, message=None):
|
||||||
|
self.options = options
|
||||||
|
if message is not None:
|
||||||
|
self.message = message
|
||||||
|
|
||||||
|
def __call__(self, value):
|
||||||
|
"""
|
||||||
|
Validates that the input is in the options list.
|
||||||
|
"""
|
||||||
|
if value in self.options:
|
||||||
|
return value
|
||||||
|
else:
|
||||||
|
raise ValidationError(self.message)
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -17,7 +17,7 @@ from os import makedirs
|
|||||||
from glob import glob
|
from glob import glob
|
||||||
|
|
||||||
try:
|
try:
|
||||||
basestring
|
basestring = basestring
|
||||||
except NameError:
|
except NameError:
|
||||||
basestring = str
|
basestring = str
|
||||||
|
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
_build/*
|
|
||||||
Vendored
Vendored
@@ -1,3 +0,0 @@
|
|||||||
*.pyc
|
|
||||||
*.pyo
|
|
||||||
.DS_Store
|
|
||||||
+2
-2
@@ -19,7 +19,7 @@ import clint
|
|||||||
# If extensions (or modules to document with autodoc) are in another directory,
|
# If extensions (or modules to document with autodoc) are in another directory,
|
||||||
# add these directories to sys.path here. If the directory is relative to the
|
# add these directories to sys.path here. If the directory is relative to the
|
||||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||||
sys.path.append(os.path.abspath('_themes'))
|
sys.path.append(os.path.abspath('themes'))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -104,7 +104,7 @@ html_theme = 'kr'
|
|||||||
#html_theme_options = {}
|
#html_theme_options = {}
|
||||||
|
|
||||||
# Add any paths that contain custom themes here, relative to this directory.
|
# Add any paths that contain custom themes here, relative to this directory.
|
||||||
html_theme_path = ['_themes']
|
html_theme_path = ['themes']
|
||||||
|
|
||||||
# The name for this set of Sphinx documents. If None, it defaults to
|
# The name for this set of Sphinx documents. If None, it defaults to
|
||||||
# "<project> v<release> documentation".
|
# "<project> v<release> documentation".
|
||||||
|
|||||||
+3
-1
@@ -1,6 +1,8 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from __future__ import print_function
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
|
||||||
@@ -18,5 +20,5 @@ with indent(4, quote='>>>'):
|
|||||||
puts(colored.red('NOT Files detected: ') + str(args.not_files))
|
puts(colored.red('NOT Files detected: ') + str(args.not_files))
|
||||||
puts(colored.red('Grouped Arguments: ') + str(dict(args.grouped)))
|
puts(colored.red('Grouped Arguments: ') + str(dict(args.grouped)))
|
||||||
|
|
||||||
print
|
print()
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
#! /usr/bin/env python
|
#! /usr/bin/env python
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.abspath('..'))
|
||||||
|
|
||||||
from clint.arguments import Args
|
from clint.arguments import Args
|
||||||
from clint.textui import puts, colored
|
from clint.textui import puts, colored
|
||||||
|
|
||||||
@@ -8,6 +13,6 @@ all_args = Args().grouped
|
|||||||
|
|
||||||
for item in all_args:
|
for item in all_args:
|
||||||
if item is not '_':
|
if item is not '_':
|
||||||
puts(colored.red("key:%s"%item))
|
puts(colored.red("key:%s" % item))
|
||||||
print(all_args[item].all)
|
print(all_args[item].all)
|
||||||
|
|
||||||
|
|||||||
+8
-1
@@ -15,7 +15,14 @@ if __name__ == '__main__':
|
|||||||
# Set validators to an empty list for an optional input
|
# Set validators to an empty list for an optional input
|
||||||
language = prompt.query("Your favorite tool (optional)?", validators=[])
|
language = prompt.query("Your favorite tool (optional)?", validators=[])
|
||||||
|
|
||||||
|
# Shows a list of options to select from
|
||||||
|
inst_options = [{'selector':'1','prompt':'Full','return':'full'},
|
||||||
|
{'selector':'2','prompt':'Partial','return':'partial'},
|
||||||
|
{'selector':'3','prompt':'None','return':'no install'}]
|
||||||
|
inst = prompt.options("Full or Partial Install", inst_options)
|
||||||
|
|
||||||
# Use a default value and a validator
|
# Use a default value and a validator
|
||||||
path = prompt.query('Installation Path', default='/usr/local/bin/', validators=[validators.PathValidator()])
|
path = prompt.query('Installation Path', default='/usr/local/bin/', validators=[validators.PathValidator()])
|
||||||
|
|
||||||
puts(colored.blue('Hi {0}. Install {1} to {2}'.format(name, language or 'nothing', path)))
|
puts(colored.blue('Hi {0}. Install {1} {2} to {3}'.format(name, inst, language or 'nothing', path)))
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ required = ['args']
|
|||||||
setup(
|
setup(
|
||||||
name='clint',
|
name='clint',
|
||||||
version=clint.__version__,
|
version=clint.__version__,
|
||||||
description='Python Command-line Application Tools',
|
description='Python Command Line Interface Tools',
|
||||||
long_description=open('README.rst').read() + '\n\n' +
|
long_description=open('README.rst').read() + '\n\n' +
|
||||||
open('HISTORY.rst').read(),
|
open('HISTORY.rst').read(),
|
||||||
author='Kenneth Reitz',
|
author='Kenneth Reitz',
|
||||||
@@ -37,7 +37,7 @@ setup(
|
|||||||
],
|
],
|
||||||
install_requires=required,
|
install_requires=required,
|
||||||
license='ISC',
|
license='ISC',
|
||||||
classifiers=(
|
classifiers=[
|
||||||
# 'Development Status :: 5 - Production/Stable',
|
# 'Development Status :: 5 - Production/Stable',
|
||||||
'Environment :: Console',
|
'Environment :: Console',
|
||||||
'Intended Audience :: Developers',
|
'Intended Audience :: Developers',
|
||||||
@@ -50,6 +50,9 @@ setup(
|
|||||||
'Programming Language :: Python :: 3',
|
'Programming Language :: Python :: 3',
|
||||||
'Programming Language :: Python :: 3.1',
|
'Programming Language :: Python :: 3.1',
|
||||||
'Programming Language :: Python :: 3.2',
|
'Programming Language :: Python :: 3.2',
|
||||||
|
'Programming Language :: Python :: 3.3',
|
||||||
|
'Programming Language :: Python :: 3.4',
|
||||||
'Topic :: Terminals :: Terminal Emulators/X Terminals',
|
'Topic :: Terminals :: Terminal Emulators/X Terminals',
|
||||||
),
|
],
|
||||||
|
test_suite='test_clint',
|
||||||
)
|
)
|
||||||
|
|||||||
+31
-1
@@ -49,7 +49,6 @@ class ColoredStringTestCase(unittest.TestCase):
|
|||||||
new_str = ColoredString('RED', '\xe4')
|
new_str = ColoredString('RED', '\xe4')
|
||||||
assert '\xe4' in str(new_str)
|
assert '\xe4' in str(new_str)
|
||||||
from clint.textui import puts
|
from clint.textui import puts
|
||||||
puts(new_str)
|
|
||||||
|
|
||||||
def test_clint_force_color_env_var(self):
|
def test_clint_force_color_env_var(self):
|
||||||
from clint.textui.colored import ColoredString
|
from clint.textui.colored import ColoredString
|
||||||
@@ -57,6 +56,37 @@ class ColoredStringTestCase(unittest.TestCase):
|
|||||||
new_str = ColoredString('RED', 'hello world')
|
new_str = ColoredString('RED', 'hello world')
|
||||||
assert new_str.always_color == True
|
assert new_str.always_color == True
|
||||||
|
|
||||||
|
def test_clint_unicode_radd(self):
|
||||||
|
from clint.textui.colored import ColoredString
|
||||||
|
inp_str = u'hello \u263A'
|
||||||
|
new_str = u'' + ColoredString('RED', inp_str)
|
||||||
|
assert inp_str.encode('utf-8') in new_str
|
||||||
|
|
||||||
|
|
||||||
|
class TextuiFormatterTestCase(unittest.TestCase):
|
||||||
|
|
||||||
|
def test_max_width(self):
|
||||||
|
|
||||||
|
def _test_n_rows_width(ins, rows, n_rows, max_width):
|
||||||
|
ins.assertEqual(len(rows), n_rows)
|
||||||
|
for row in rows:
|
||||||
|
ins.assertLessEqual(len(row), max_width)
|
||||||
|
|
||||||
|
from clint.textui.formatters import max_width
|
||||||
|
from clint.textui import colored
|
||||||
|
# normal text
|
||||||
|
text = ' '.join(['XXX'] * 3)
|
||||||
|
rows = max_width(text, 6).split('\n')
|
||||||
|
_test_n_rows_width(self, rows, 3, 6)
|
||||||
|
rows = max_width(text, 7).split('\n')
|
||||||
|
_test_n_rows_width(self, rows, 2, 7)
|
||||||
|
# colored text
|
||||||
|
c_text = colored.yellow(text)
|
||||||
|
rows = max_width(c_text, 6).split('\n')
|
||||||
|
_test_n_rows_width(self, rows, 3, 6)
|
||||||
|
rows = max_width(c_text, 7).split('\n')
|
||||||
|
_test_n_rows_width(self, rows, 2, 7)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
Reference in New Issue
Block a user