mirror of
https://github.com/kennethreitz/clint.git
synced 2026-06-05 14:50:17 +00:00
Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 57955b9156 | |||
| f1ab574413 | |||
| 2aff70618c | |||
| 4b7688b220 | |||
| 03731d6ef2 | |||
| 8a2aed6c32 | |||
| 98cab56e97 | |||
| 13df32316f | |||
| 93c3f9a4e1 | |||
| 9f14dbb6c5 | |||
| 0e0284f967 | |||
| c5dbe0708e | |||
| 00b522510c | |||
| 4b98c46519 | |||
| e62f5657a5 | |||
| 8e17e8ce50 | |||
| 84dafbeabe | |||
| 5305d1e116 | |||
| 73b1bdd2d4 | |||
| 7bde48ce53 | |||
| a0e14e4181 | |||
| de2cbfca90 | |||
| c909d7dca0 | |||
| 6220552ebf | |||
| a9623213e3 | |||
| 77eab0c0e6 | |||
| 3d01b405b3 | |||
| 6c50b92cb3 | |||
| e00476d195 | |||
| fc463f16c3 | |||
| 9324af95b2 | |||
| 50294ea2dc | |||
| 56fe6138b6 | |||
| d22847fb95 | |||
| ce25cea7e8 |
@@ -1,5 +1,11 @@
|
||||
.idea
|
||||
.project
|
||||
.settings
|
||||
.pydevproject
|
||||
MANIFEST
|
||||
*.pyc
|
||||
.tox
|
||||
build/
|
||||
clint.egg-info/
|
||||
dist/
|
||||
|
||||
|
||||
@@ -6,6 +6,10 @@ Development Lead
|
||||
|
||||
- Kenneth Reitz <me@kennethreitz.com>
|
||||
|
||||
Maintainers
|
||||
```````````
|
||||
|
||||
- Jason Piper <j.piper@me.com>
|
||||
|
||||
Patches and Suggestions
|
||||
```````````````````````
|
||||
@@ -25,3 +29,4 @@ Patches and Suggestions
|
||||
- Justin Barber <barber.justin (at) gmail>
|
||||
- Dmitry Medvinsky
|
||||
- Eric Anderson
|
||||
- Joshua Richardson
|
||||
|
||||
+32
@@ -1,6 +1,38 @@
|
||||
History
|
||||
-------
|
||||
|
||||
0.3.7
|
||||
+++++
|
||||
* Clint now obeys the CLINT_FORCE_COLOR environmental variable
|
||||
|
||||
0.3.6
|
||||
+++++
|
||||
* Fixed faulty PyPI deployment
|
||||
|
||||
0.3.5
|
||||
+++++
|
||||
* progress.bar is now a context manager - doesn't require an iterable anymore (thanks to @jric)
|
||||
* Bug fixes
|
||||
|
||||
0.3.4
|
||||
+++++
|
||||
* Fixed Python 3 basestring deprecation
|
||||
* Fixed examples
|
||||
|
||||
0.3.3
|
||||
+++++
|
||||
* Fixed Python 3 build issues
|
||||
* Fixed README and HISTORY being installed to /usr
|
||||
* Support added for bold text
|
||||
|
||||
0.3.2
|
||||
+++++
|
||||
* Unknown
|
||||
|
||||
0.3.1
|
||||
+++++
|
||||
* Unknown
|
||||
|
||||
0.3.0
|
||||
+++++
|
||||
|
||||
|
||||
+8
-3
@@ -91,7 +91,9 @@ I want to get data piped to stdin. ::
|
||||
|
||||
I want to get the first commandline argument passed in. ::
|
||||
|
||||
>>> clint.args.get(0)
|
||||
>>> from clint import arguments
|
||||
>>> args = arguments.Args()
|
||||
>>> args.get(0)
|
||||
|
||||
# if no argument was passed, get returns None
|
||||
|
||||
@@ -107,6 +109,10 @@ I want to store a configuration file. ::
|
||||
# Windows: 'C:\\Users\\appuser\\AppData\\Local\\Company\\AppName\\config.ini'
|
||||
# Linux: '/home/appuser/.config/appname/config.ini'
|
||||
|
||||
I want to force color output even if stdout is not a TTY:
|
||||
|
||||
$ export CLINT_FORCE_COLOR=1
|
||||
|
||||
|
||||
Installation
|
||||
------------
|
||||
@@ -147,7 +153,7 @@ Contribute
|
||||
----------
|
||||
|
||||
If you'd like to contribute, simply fork `the repository`_, commit your changes
|
||||
to the **develop** branch (or branch off of it), and send a pull request. Make
|
||||
to the **master** branch (or branch off of it), and send a pull request. Make
|
||||
sure you add yourself to AUTHORS_.
|
||||
|
||||
|
||||
@@ -155,7 +161,6 @@ Roadmap
|
||||
-------
|
||||
- Unittests
|
||||
- Sphinx Documentation
|
||||
- Python 2.5, 3.1, 3.2 Support
|
||||
|
||||
|
||||
|
||||
|
||||
+2
-2
@@ -26,8 +26,8 @@ from .pipes import piped_in
|
||||
|
||||
|
||||
__title__ = 'clint'
|
||||
__version__ = '0.3.1'
|
||||
__build__ = 0x000301
|
||||
__version__ = '0.3.7'
|
||||
__build__ = 0x000307
|
||||
__author__ = 'Kenneth Reitz'
|
||||
__license__ = 'ISC'
|
||||
__copyright__ = 'Copyright 2012 Kenneth Reitz'
|
||||
|
||||
+40
-29
@@ -11,6 +11,7 @@ This module provides a simple and elegant wrapper for colorama.
|
||||
|
||||
from __future__ import absolute_import
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
@@ -37,29 +38,36 @@ else:
|
||||
DISABLE_COLOR = False
|
||||
|
||||
|
||||
|
||||
class ColoredString(object):
|
||||
"""Enhanced string for __len__ operations on Colored output."""
|
||||
def __init__(self, color, s, always_color=False):
|
||||
def __init__(self, color, s, always_color=False, bold=False):
|
||||
super(ColoredString, self).__init__()
|
||||
self.s = s
|
||||
self.color = color
|
||||
self.always_color = always_color
|
||||
self.bold = bold
|
||||
if os.environ.get('CLINT_FORCE_COLOR'):
|
||||
self.always_color = True
|
||||
|
||||
def __getattr__(self, att):
|
||||
def func_help(*args, **kwargs):
|
||||
result = getattr(self.s, att)(*args, **kwargs)
|
||||
if isinstance(result, basestring):
|
||||
return self._new(result)
|
||||
elif isinstance(result, list):
|
||||
return [self._new(x) for x in result]
|
||||
else:
|
||||
return result
|
||||
return func_help
|
||||
def func_help(*args, **kwargs):
|
||||
result = getattr(self.s, att)(*args, **kwargs)
|
||||
try:
|
||||
is_result_string = isinstance(result, basestring)
|
||||
except NameError:
|
||||
is_result_string = isinstance(result, str)
|
||||
if is_result_string:
|
||||
return self._new(result)
|
||||
elif isinstance(result, list):
|
||||
return [self._new(x) for x in result]
|
||||
else:
|
||||
return result
|
||||
return func_help
|
||||
|
||||
@property
|
||||
def color_str(self):
|
||||
c = '%s%s%s' % (getattr(colorama.Fore, self.color), self.s, colorama.Fore.RESET)
|
||||
style = 'BRIGHT' if self.bold else 'NORMAL'
|
||||
c = '%s%s%s%s%s' % (getattr(colorama.Fore, self.color), getattr(colorama.Style, style), self.s, colorama.Fore.RESET, getattr(colorama.Style, 'NORMAL'))
|
||||
|
||||
if self.always_color:
|
||||
return c
|
||||
@@ -85,7 +93,10 @@ class ColoredString(object):
|
||||
__str__ = __unicode__
|
||||
else:
|
||||
def __str__(self):
|
||||
return unicode(self).encode('utf8')
|
||||
value = self.color_str
|
||||
if isinstance(value, bytes):
|
||||
return value
|
||||
return value.encode('utf8')
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self.color_str)
|
||||
@@ -113,29 +124,29 @@ def clean(s):
|
||||
return txt
|
||||
|
||||
|
||||
def black(string, always=False):
|
||||
return ColoredString('BLACK', string, always_color=always)
|
||||
def black(string, always=False, bold=False):
|
||||
return ColoredString('BLACK', string, always_color=always, bold=bold)
|
||||
|
||||
def red(string, always=False):
|
||||
return ColoredString('RED', string, always_color=always)
|
||||
def red(string, always=False, bold=False):
|
||||
return ColoredString('RED', string, always_color=always, bold=bold)
|
||||
|
||||
def green(string, always=False):
|
||||
return ColoredString('GREEN', string, always_color=always)
|
||||
def green(string, always=False, bold=False):
|
||||
return ColoredString('GREEN', string, always_color=always, bold=bold)
|
||||
|
||||
def yellow(string, always=False):
|
||||
return ColoredString('YELLOW', string, always_color=always)
|
||||
def yellow(string, always=False, bold=False):
|
||||
return ColoredString('YELLOW', string, always_color=always, bold=bold)
|
||||
|
||||
def blue(string, always=False):
|
||||
return ColoredString('BLUE', string, always_color=always)
|
||||
def blue(string, always=False, bold=False):
|
||||
return ColoredString('BLUE', string, always_color=always, bold=bold)
|
||||
|
||||
def magenta(string, always=False):
|
||||
return ColoredString('MAGENTA', string, always_color=always)
|
||||
def magenta(string, always=False, bold=False):
|
||||
return ColoredString('MAGENTA', string, always_color=always, bold=bold)
|
||||
|
||||
def cyan(string, always=False):
|
||||
return ColoredString('CYAN', string, always_color=always)
|
||||
def cyan(string, always=False, bold=False):
|
||||
return ColoredString('CYAN', string, always_color=always, bold=bold)
|
||||
|
||||
def white(string, always=False):
|
||||
return ColoredString('WHITE', string, always_color=always)
|
||||
def white(string, always=False, bold=False):
|
||||
return ColoredString('WHITE', string, always_color=always, bold=bold)
|
||||
|
||||
def disable():
|
||||
"""Disables colors."""
|
||||
|
||||
+68
-33
@@ -33,43 +33,78 @@ ETA_INTERVAL = 1
|
||||
#How many intervals (excluding the current one) to calculate the simple moving average
|
||||
ETA_SMA_WINDOW = 9
|
||||
|
||||
def bar(it, label='', width=32, hide=HIDE_DEFAULT, empty_char=BAR_EMPTY_CHAR, filled_char=BAR_FILLED_CHAR, expected_size=None, every=1):
|
||||
"""Progress iterator. Wrap your iterables with it."""
|
||||
|
||||
def _show(_i):
|
||||
if (time.time() - bar.etadelta) > ETA_INTERVAL:
|
||||
bar.etadelta = time.time()
|
||||
bar.ittimes = bar.ittimes[-ETA_SMA_WINDOW:]+[-(bar.start-time.time())/(_i+1)]
|
||||
bar.eta = sum(bar.ittimes)/float(len(bar.ittimes)) * (count-_i)
|
||||
bar.etadisp = time.strftime('%H:%M:%S', time.gmtime(bar.eta))
|
||||
x = int(width*_i/count)
|
||||
if not hide:
|
||||
if ((_i % every)==0 or # True every "every" updates
|
||||
(_i == count)): # And when we're done
|
||||
class Bar(object):
|
||||
def __enter__(self):
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
self.done()
|
||||
return False # we're not surpressing exceptions
|
||||
|
||||
def __init__(self, label='', width=32, hide=None, empty_char=BAR_EMPTY_CHAR,
|
||||
filled_char=BAR_FILLED_CHAR, expected_size=None, every=1):
|
||||
self.label = label
|
||||
self.width = width
|
||||
self.hide = hide
|
||||
if hide is None:
|
||||
try:
|
||||
self.hide = not STREAM.isatty()
|
||||
except AttributeError: # output does not support isatty()
|
||||
self.hide = True
|
||||
self.empty_char = empty_char
|
||||
self.filled_char = filled_char
|
||||
self.expected_size = expected_size
|
||||
self.every = every
|
||||
self.start = time.time()
|
||||
self.ittimes = []
|
||||
self.eta = 0
|
||||
self.etadelta = time.time()
|
||||
self.etadisp = time.strftime('%H:%M:%S', time.gmtime(self.eta))
|
||||
if (self.expected_size):
|
||||
self.show(0)
|
||||
|
||||
def show(self, progress, count=None):
|
||||
if count is not None:
|
||||
self.expected_size = count
|
||||
if self.expected_size is None:
|
||||
raise Exception("expected_size not initialized")
|
||||
if (time.time() - self.etadelta) > ETA_INTERVAL:
|
||||
self.etadelta = time.time()
|
||||
self.ittimes = \
|
||||
self.ittimes[-ETA_SMA_WINDOW:]+\
|
||||
[-(self.start-time.time())/(progress+1)]
|
||||
self.eta = \
|
||||
sum(self.ittimes)/float(len(self.ittimes)) * \
|
||||
(self.expected_size-progress)
|
||||
self.etadisp = time.strftime('%H:%M:%S', time.gmtime(self.eta))
|
||||
x = int(self.width*progress/self.expected_size)
|
||||
if not self.hide:
|
||||
if ((progress % self.every)==0 or # True every "every" updates
|
||||
(progress == self.expected_size)): # And when we're done
|
||||
STREAM.write(BAR_TEMPLATE % (
|
||||
label, filled_char*x, empty_char*(width-x), _i, count, bar.etadisp))
|
||||
self.label, self.filled_char*x,
|
||||
self.empty_char*(self.width-x), progress,
|
||||
self.expected_size, self.etadisp))
|
||||
STREAM.flush()
|
||||
|
||||
count = len(it) if expected_size is None else expected_size
|
||||
|
||||
bar.start = time.time()
|
||||
bar.ittimes = []
|
||||
bar.eta = 0
|
||||
bar.etadelta = time.time()
|
||||
bar.etadisp = time.strftime('%H:%M:%S', time.gmtime(bar.eta))
|
||||
|
||||
if count:
|
||||
_show(0)
|
||||
|
||||
for i, item in enumerate(it):
|
||||
|
||||
yield item
|
||||
_show(i+1)
|
||||
|
||||
if not hide:
|
||||
def done(self):
|
||||
if not self.hide:
|
||||
STREAM.write('\n')
|
||||
STREAM.flush()
|
||||
|
||||
def bar(it, label='', width=32, hide=HIDE_DEFAULT, empty_char=BAR_EMPTY_CHAR, filled_char=BAR_FILLED_CHAR, expected_size=None, every=1):
|
||||
"""Progress iterator. Wrap your iterables with it."""
|
||||
|
||||
count = len(it) if expected_size is None else expected_size
|
||||
|
||||
with Bar(label=label, width=width, hide=hide, empty_char=BAR_EMPTY_CHAR,
|
||||
filled_char=BAR_FILLED_CHAR, expected_size=count, every=every) \
|
||||
as bar:
|
||||
|
||||
for i, item in enumerate(it):
|
||||
|
||||
yield item
|
||||
bar.show(i+1)
|
||||
|
||||
def dots(it, label='', hide=HIDE_DEFAULT, every=1):
|
||||
"""Progress iterator. Prints a dot for each item being iterated"""
|
||||
@@ -97,10 +132,10 @@ def mill(it, label='', hide=HIDE_DEFAULT, expected_size=None, every=1):
|
||||
"""Progress iterator. Prints a mill while iterating over the items."""
|
||||
|
||||
def _mill_char(_i):
|
||||
if _i == 100:
|
||||
if _i >= count:
|
||||
return ' '
|
||||
else:
|
||||
return MILL_CHARS[_i % len(MILL_CHARS)]
|
||||
return MILL_CHARS[(_i / every) % len(MILL_CHARS)]
|
||||
|
||||
def _show(_i):
|
||||
if not hide:
|
||||
|
||||
+13
-8
@@ -8,29 +8,34 @@ Module for simple interactive prompts handling
|
||||
|
||||
"""
|
||||
|
||||
from __future__ import absolute_import
|
||||
from __future__ import absolute_import, print_function
|
||||
|
||||
from re import match, I
|
||||
try:
|
||||
raw_input
|
||||
except NameError:
|
||||
raw_input = input
|
||||
|
||||
|
||||
def yn(prompt, default='y', batch=False):
|
||||
# A sanity check against default value
|
||||
# If not y/n then y is assumed
|
||||
# If not y/n then y is assumed
|
||||
if default not in ['y', 'n']:
|
||||
default = 'y'
|
||||
|
||||
|
||||
# Let's build the prompt
|
||||
choicebox = '[Y/n]' if default == 'y' else '[y/N]'
|
||||
prompt = prompt + ' ' + choicebox + ' '
|
||||
choicebox = '[Y/n]' if default == 'y' else '[y/N]'
|
||||
prompt = prompt + ' ' + choicebox + ' '
|
||||
|
||||
# If input is not a yes/no variant or empty
|
||||
# keep asking
|
||||
while True:
|
||||
# If batch option is True then auto reply
|
||||
# If batch option is True then auto reply
|
||||
# with default input
|
||||
if not batch:
|
||||
input = raw_input(prompt).strip()
|
||||
else:
|
||||
print prompt
|
||||
print(prompt)
|
||||
input = ''
|
||||
|
||||
# If input is empty default choice is assumed
|
||||
@@ -39,7 +44,7 @@ def yn(prompt, default='y', batch=False):
|
||||
return True
|
||||
|
||||
# Given 'yes' as input if default choice is y
|
||||
# then return True, False otherwise
|
||||
# then return True, False otherwise
|
||||
if match('y(?:es)?', input, I):
|
||||
return True if default == 'y' else False
|
||||
|
||||
|
||||
+7
-29
@@ -61,37 +61,15 @@ def mkdir_p(path):
|
||||
|
||||
def tsplit(string, delimiters):
|
||||
"""Behaves str.split but supports tuples of delimiters."""
|
||||
|
||||
delimiters = tuple(delimiters)
|
||||
stack = [string,]
|
||||
if len(delimiters) < 1:
|
||||
return [string,]
|
||||
final_delimiter = delimiters[0]
|
||||
for i in delimiters[1:]:
|
||||
string = string.replace(i, final_delimiter)
|
||||
return string.split(final_delimiter)
|
||||
|
||||
for delimiter in delimiters:
|
||||
for i, substring in enumerate(stack):
|
||||
substack = substring.split(delimiter)
|
||||
stack.pop(i)
|
||||
for j, _substring in enumerate(substack):
|
||||
stack.insert(i+j, _substring)
|
||||
|
||||
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
|
||||
return [string[i:i+size] for i in range(0, len(string), size)]
|
||||
|
||||
Regular → Executable
+3
-1
@@ -6,9 +6,11 @@ import os
|
||||
|
||||
sys.path.insert(0, os.path.abspath('..'))
|
||||
|
||||
from clint import args
|
||||
from clint.arguments import Args
|
||||
from clint.textui import puts, colored, indent
|
||||
|
||||
args = Args()
|
||||
|
||||
with indent(4, quote='>>>'):
|
||||
puts(colored.red('Aruments passed in: ') + str(args.all))
|
||||
puts(colored.red('Flags detected: ') + str(args.flags))
|
||||
|
||||
Regular → Executable
+2
-2
@@ -1,10 +1,10 @@
|
||||
#! /usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from clint import args
|
||||
from clint.arguments import Args
|
||||
from clint.textui import puts, colored
|
||||
|
||||
all_args = args.grouped
|
||||
all_args = Args().grouped
|
||||
|
||||
for item in all_args:
|
||||
if item is not '_':
|
||||
|
||||
@@ -14,6 +14,13 @@ from clint.textui import progress
|
||||
if __name__ == '__main__':
|
||||
for i in progress.bar(range(100)):
|
||||
sleep(random() * 0.2)
|
||||
|
||||
with progress.Bar(label="nonlinear", expected_size=10) as bar:
|
||||
last_val = 0
|
||||
for val in (1,2,3,9,10):
|
||||
sleep(2 * (val - last_val))
|
||||
bar.show(val)
|
||||
last_val = val
|
||||
|
||||
for i in progress.dots(range(100)):
|
||||
sleep(random() * 0.2)
|
||||
|
||||
+3
-1
@@ -11,10 +11,12 @@ try:
|
||||
except:
|
||||
import simplejson as json
|
||||
|
||||
from clint import args
|
||||
from clint.arguments import Args
|
||||
from clint import piped_in
|
||||
from clint.textui import colored, puts, indent
|
||||
|
||||
args = Args()
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
puts('Test:')
|
||||
|
||||
@@ -11,8 +11,6 @@ except ImportError:
|
||||
|
||||
import clint
|
||||
|
||||
|
||||
|
||||
def publish():
|
||||
"""Publish to PyPi"""
|
||||
os.system("python setup.py sdist upload")
|
||||
@@ -32,10 +30,6 @@ setup(
|
||||
author='Kenneth Reitz',
|
||||
author_email='me@kennethreitz.com',
|
||||
url='https://github.com/kennethreitz/clint',
|
||||
data_files=[
|
||||
'README.rst',
|
||||
'HISTORY.rst',
|
||||
],
|
||||
packages= [
|
||||
'clint',
|
||||
'clint.textui',
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
"""Clint Test Suite."""
|
||||
|
||||
import os
|
||||
import unittest
|
||||
|
||||
|
||||
@@ -42,5 +43,20 @@ class ColoredStringTestCase(unittest.TestCase):
|
||||
output = new_str.replace("world", "universe")
|
||||
assert output.s == "hello universe"
|
||||
|
||||
def test_py2_bytes_not_mangled(self):
|
||||
from clint.textui.colored import ColoredString
|
||||
# On python 2 make sure the same bytes come out as went in
|
||||
new_str = ColoredString('RED', '\xe4')
|
||||
assert '\xe4' in str(new_str)
|
||||
from clint.textui import puts
|
||||
puts(new_str)
|
||||
|
||||
def test_clint_force_color_env_var(self):
|
||||
from clint.textui.colored import ColoredString
|
||||
os.environ['CLINT_FORCE_COLOR'] = "1"
|
||||
new_str = ColoredString('RED', 'hello world')
|
||||
assert new_str.always_color == True
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
Reference in New Issue
Block a user