Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix issue 157 #160

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,16 @@ git:
depth: 5
matrix:
include:
- python: '3.5'
env: TOXENV=lint
- python: '3.5'
env: TOXENV=py35
- python: '3.6'
env: TOXENV=py36
- python: '3.7'
env: TOXENV=py37
- python: '3.8'
env: TOXENV=py38
- python: "3.5"
env: TOXENV=lint
- python: "3.5"
env: TOXENV=py35
- python: "3.6"
env: TOXENV=py36
- python: "3.7"
env: TOXENV=py37
- python: "3.8"
env: TOXENV=py38
fast_finish: true
install:
- pip install tox coveralls
Expand Down
2 changes: 1 addition & 1 deletion examples/colored_text_spin.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,6 @@
spinner.spinner = 'hearts'
spinner.text_color = 'magenta'
time.sleep(2)
spinner.stop_and_persist(symbol='🦄 '.encode('utf-8'), text='Wow!')
spinner.stop_and_persist(symbol='🦄'.encode('utf-8'), text='Wow!')
except (KeyboardInterrupt, SystemExit):
spinner.stop()
22 changes: 22 additions & 0 deletions examples/dict_text_loop.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from halo import Halo
from time import sleep


@Halo(text='Loading {task}', spinner='line')
def run_task(halo_iter=[], stop_text='', stop_symbol=' '):
sleep(1)

@Halo(text='Loading {task} at {task2}', spinner='line')
def run_task2(halo_iter=[], stop_text='', stop_symbol=' '):
sleep(1)

tasks1 = ['breakfest', 'launch', 'dinner']
tasks2 = ['morning', 'noon', 'night']

#with symbol
run_task(halo_iter=tasks1, stop_symbol='🦄'.encode(
'utf-8'), stop_text='Task1 Finished')

#without symbol
run_task2(halo_iter=list(zip(tasks1, tasks2)), stop_text='Finished Time')
run_task2.spinner.symbol = ''
2 changes: 2 additions & 0 deletions halo/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
__author__ = 'Manraj Singh'
__email__ = '[email protected]'

all = ["cursor.py", "halo_notebook.py", "halo.py"]

import logging

from .halo import Halo
Expand Down
21 changes: 5 additions & 16 deletions halo/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,8 @@ def is_supported():

os_arch = platform.system()

if os_arch != 'Windows':
return True
return True if os_arch != 'Windows' else False

return False


def get_environment():
Expand All @@ -42,10 +40,7 @@ def get_environment():
"""
try:
from IPython import get_ipython
except ImportError:
return 'terminal'

try:

shell = get_ipython().__class__.__name__

if shell == 'ZMQInteractiveShell': # Jupyter notebook or qtconsole
Expand All @@ -55,7 +50,7 @@ def get_environment():
else:
return 'terminal' # Other type (?)

except NameError:
except (ImportError, NameError):
return 'terminal'


Expand Down Expand Up @@ -90,10 +85,7 @@ def is_text_type(text):
bool
Whether parameter is a string or not
"""
if isinstance(text, six.text_type) or isinstance(text, six.string_types):
return True

return False
return True if isinstance(text, (six.text_type, six.string_types)) else False


def decode_utf_8_text(text):
Expand Down Expand Up @@ -146,7 +138,4 @@ def get_terminal_columns():

# If column size is 0 either we are not connected
# to a terminal or something else went wrong. Fallback to 80.
if terminal_size.columns == 0:
return 80
else:
return terminal_size.columns
return 80 if terminal_size.columns == 0 else terminal_size.columns
3 changes: 2 additions & 1 deletion halo/cursor.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@
import ctypes

class _CursorInfo(ctypes.Structure):
_fields_ = [("size", ctypes.c_int), ("visible", ctypes.c_byte)]
_fields_ = [("size", ctypes.c_int),
("visible", ctypes.c_byte)]


def hide(stream=sys.stdout):
Expand Down
104 changes: 93 additions & 11 deletions halo/halo.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
import threading
import time

import re


import halo.cursor as cursor

from log_symbols.symbols import LogSymbols
Expand Down Expand Up @@ -77,6 +80,18 @@ def __init__(
stream : io, optional
Output.
"""

# To reset Values in deleter
self.reset_values = {"text": text,
"color": color,
"text_color": text_color,
"spinner": spinner,
"animation": animation,
"placement": placement, }

self._symbol = " "
self._stop_persist = False

self._color = color
self._animation = animation

Expand Down Expand Up @@ -120,19 +135,62 @@ def __enter__(self):
return self.start()

def __exit__(self, type, value, traceback):
"""Stops the spinner. For use in context managers."""
self.stop()
"""Stops the spinner with show text at the end or not. For use in context managers."""
if self._stop_persist:
self.stop_and_persist(symbol=self._symbol, text=self.text)
else:
self.stop()

def __call__(self, f):
"""Allow the Halo object to be used as a regular function decorator."""

@functools.wraps(f)
def wrapped(*args, **kwargs):
with self:
self._change_text(f, *args, **kwargs)
return f(*args, **kwargs)

return wrapped

def _change_text(self, f, *args, **kwargs):
"""if you want to change text in decorator as your function is in a loop
* you have to use halo_iter as the argument of function
* if you want to show finished text use stop_persist:bool, stop_text:str and stop_symbol:str

Args:
f (callable): the function which supposed to be in a loop
"""
if "halo_iter" in kwargs:
if type(kwargs['halo_iter']) in [list, tuple, dict]:
main_text = self.text # text have curl-brackets like
# 'This is task {number}'
curl_brackets = re.findall(
r'\{([^\s\{\}]+)\}', main_text)
results = [] # store all return f(*args, **kwargs) in loop
for text in kwargs['halo_iter']:
#* type(text) is str in single curl-bracket
#* or list[str] in multiple curl-brackets
text_dict = dict(list(zip(curl_brackets, text))) if len(
curl_brackets) > 1 else dict([(curl_brackets[0], text)])

self.text = main_text.format(**text_dict)
results.append(f(*args, **kwargs))

if 'stop_text' in kwargs:
self._stop_persist = True
self.text = kwargs['stop_text']

if 'stop_symbol' in kwargs:
self._stop_persist = True
self._symbol = kwargs['stop_symbol']
else:
self._symbol = ' '

return results

else:
self._stop_persist = False

@property
def spinner(self):
"""Getter for spinner property.
Expand All @@ -156,6 +214,12 @@ def spinner(self, spinner=None):
self._frame_index = 0
self._text_index = 0

@spinner.deleter
def spinner(self):
"""set spinner to None when delete spinner is
"""
self._spinner = self.reset_values["spinner"]

@property
def text(self):
"""Getter for text property.
Expand All @@ -176,6 +240,10 @@ def text(self, text):
"""
self._text = self._get_text(text)

@text.deleter
def text(self):
self._text = self.reset_values["text"]

@property
def text_color(self):
"""Getter for text color property.
Expand All @@ -196,6 +264,10 @@ def text_color(self, text_color):
"""
self._text_color = text_color

@text_color.deleter
def text_color(self):
self._text_color = self.reset_values["text_color"]

@property
def color(self):
"""Getter for color property.
Expand All @@ -216,6 +288,10 @@ def color(self, color):
"""
self._color = color

@color.deleter
def color(self):
self._color = self.reset_values["color"]

@property
def placement(self):
"""Getter for placement property.
Expand All @@ -242,6 +318,10 @@ def placement(self, placement):
)
self._placement = placement

@placement.deleter
def placement(self):
self.placement = self.reset_values["placement"]

@property
def spinner_id(self):
"""Getter for spinner id
Expand Down Expand Up @@ -273,6 +353,10 @@ def animation(self, animation):
self._animation = animation
self._text = self._get_text(self._text["original"])

@animation.deleter
def animation(self):
self._animation = self.reset_values["animation"]

def _check_stream(self):
"""Returns whether the stream is open, and if applicable, writable
Returns
Expand Down Expand Up @@ -334,10 +418,7 @@ def _get_spinner(self, spinner):
return spinner

if is_supported():
if all([is_text_type(spinner), spinner in Spinners.__members__]):
return Spinners[spinner].value
else:
return default_spinner
return Spinners[spinner].value if all([is_text_type(spinner), spinner in Spinners.__members__]) else default_spinner
else:
return Spinners["line"].value

Expand All @@ -354,8 +435,8 @@ def _get_text(self, text):
max_spinner_length = max([len(i) for i in self._spinner["frames"]])

# Subtract to the current terminal size the max spinner length
# (-1 to leave room for the extra space between spinner and text)
terminal_width = get_terminal_columns() - max_spinner_length - 1
# (+1 to leave room for the extra space between spinner and text)
terminal_width = get_terminal_columns() - (max_spinner_length + 1)
text_length = len(stripped_text)

frames = []
Expand All @@ -366,15 +447,16 @@ def _get_text(self, text):
Make the text bounce back and forth
"""
for x in range(0, text_length - terminal_width + 1):
frames.append(stripped_text[x : terminal_width + x])
frames.append(stripped_text[x: terminal_width + x])
frames.extend(list(reversed(frames)))
elif "marquee":
"""
Make the text scroll like a marquee
"""
stripped_text = stripped_text + " " + stripped_text[:terminal_width]
stripped_text = stripped_text + " " + \
stripped_text[:terminal_width]
for x in range(0, text_length + 1):
frames.append(stripped_text[x : terminal_width + x])
frames.append(stripped_text[x: terminal_width + x])
elif terminal_width < text_length and not animation:
# Add ellipsis if text is larger than terminal width and no animation was specified
frames = [stripped_text[: terminal_width - 6] + " (...)"]
Expand Down
1 change: 1 addition & 0 deletions tests/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
all = ["test_halo_notebook.py", "test_halo.py"]
Loading