1|Logo|
2
3=====================================================================
4``yaspin``: **Y**\ et **A**\ nother Terminal **Spin**\ ner for Python
5=====================================================================
6
7|Build Status| |Coverage| |pyup| |black-fmt|
8
9|pypi| |Versions| |Wheel| |Examples|
10
11|DownloadsTot| |DownloadsW|
12
13
14``Yaspin`` provides a full-featured terminal spinner to show the progress during long-hanging operations.
15
16.. image:: https://raw.githubusercontent.com/pavdmyt/yaspin/master/gifs/demo.gif
17
18It is easy to integrate into existing codebase by using it as a `context manager`_
19or as a function `decorator`_:
20
21.. code:: python
22
23    import time
24    from yaspin import yaspin
25
26    # Context manager:
27    with yaspin():
28        time.sleep(3)  # time consuming code
29
30    # Function decorator:
31    @yaspin(text="Loading...")
32    def some_operations():
33        time.sleep(3)  # time consuming code
34
35    some_operations()
36
37
38**Yaspin** also provides an intuitive and powerful API. For example, you can easily summon a shark:
39
40.. code:: python
41
42    import time
43    from yaspin import yaspin
44
45    with yaspin().white.bold.shark.on_blue as sp:
46        sp.text = "White bold shark in a blue sea"
47        time.sleep(5)
48
49.. image:: https://raw.githubusercontent.com/pavdmyt/yaspin/master/gifs/shark.gif
50
51
52Features
53--------
54
55- Runs at all major **CPython** versions (*3.6*, *3.7*, *3.8*, *3.9*), **PyPy**
56- Supports all (70+) spinners from `cli-spinners`_
57- Supports all *colors*, *highlights*, *attributes* and their mixes from `termcolor`_ library
58- Easy to combine with other command-line libraries, e.g. `prompt-toolkit`_
59- Flexible API, easy to integrate with existing code
60- User-friendly API for handling POSIX `signals`_
61- Safe **pipes** and **redirects**:
62
63.. code-block:: bash
64
65    $ python script_that_uses_yaspin.py > script.log
66    $ python script_that_uses_yaspin.py | grep ERROR
67
68
69Installation
70------------
71
72From `PyPI`_ using ``pip`` package manager:
73
74.. code-block:: bash
75
76    pip install --upgrade yaspin
77
78
79Or install the latest sources from GitHub:
80
81.. code-block:: bash
82
83    pip install https://github.com/pavdmyt/yaspin/archive/master.zip
84
85
86Usage
87-----
88
89Basic Example
90/////////////
91
92.. image:: https://raw.githubusercontent.com/pavdmyt/yaspin/master/gifs/basic_example.gif
93
94.. code:: python
95
96    import time
97    from random import randint
98    from yaspin import yaspin
99
100    with yaspin(text="Loading", color="yellow") as spinner:
101        time.sleep(2)  # time consuming code
102
103        success = randint(0, 1)
104        if success:
105            spinner.ok("✅ ")
106        else:
107            spinner.fail("�� ")
108
109
110It is also possible to control spinner manually:
111
112.. code:: python
113
114    import time
115    from yaspin import yaspin
116
117    spinner = yaspin()
118    spinner.start()
119
120    time.sleep(3)  # time consuming tasks
121
122    spinner.stop()
123
124
125Run any spinner from `cli-spinners`_
126////////////////////////////////////
127
128.. image:: https://raw.githubusercontent.com/pavdmyt/yaspin/master/gifs/cli_spinners.gif
129
130.. code:: python
131
132    import time
133    from yaspin import yaspin
134    from yaspin.spinners import Spinners
135
136    with yaspin(Spinners.earth, text="Earth") as sp:
137        time.sleep(2)                # time consuming code
138
139        # change spinner
140        sp.spinner = Spinners.moon
141        sp.text = "Moon"
142
143        time.sleep(2)                # time consuming code
144
145
146Any Colour You Like `��`_
147/////////////////////////
148
149.. image:: https://raw.githubusercontent.com/pavdmyt/yaspin/master/gifs/basic_colors.gif
150
151.. code:: python
152
153    import time
154    from yaspin import yaspin
155
156    with yaspin(text="Colors!") as sp:
157        # Support all basic termcolor text colors
158        colors = ("red", "green", "yellow", "blue", "magenta", "cyan", "white")
159
160        for color in colors:
161            sp.color, sp.text = color, color
162            time.sleep(1)
163
164
165Advanced colors usage
166/////////////////////
167
168.. image:: https://raw.githubusercontent.com/pavdmyt/yaspin/master/gifs/advanced_colors.gif
169
170.. code:: python
171
172    import time
173    from yaspin import yaspin
174    from yaspin.spinners import Spinners
175
176    text = "Bold blink magenta spinner on cyan color"
177    with yaspin().bold.blink.magenta.bouncingBall.on_cyan as sp:
178        sp.text = text
179        time.sleep(3)
180
181    # The same result can be achieved by passing arguments directly
182    with yaspin(
183        Spinners.bouncingBall,
184        color="magenta",
185        on_color="on_cyan",
186        attrs=["bold", "blink"],
187    ) as sp:
188        sp.text = text
189        time.sleep(3)
190
191
192Run any spinner you want
193////////////////////////
194
195.. image:: https://raw.githubusercontent.com/pavdmyt/yaspin/master/gifs/custom_spinners.gif
196
197.. code:: python
198
199    import time
200    from yaspin import yaspin, Spinner
201
202    # Compose new spinners with custom frame sequence and interval value
203    sp = Spinner(["��", "��", "��", "��", "��", "��", "��", "��", "��"], 200)
204
205    with yaspin(sp, text="Cat!"):
206        time.sleep(3)  # cat consuming code :)
207
208
209Change spinner properties on the fly
210////////////////////////////////////
211
212.. image:: https://raw.githubusercontent.com/pavdmyt/yaspin/master/gifs/sp_properties.gif
213
214.. code:: python
215
216    import time
217    from yaspin import yaspin
218    from yaspin.spinners import Spinners
219
220    with yaspin(Spinners.noise, text="Noise spinner") as sp:
221        time.sleep(2)
222
223        sp.spinner = Spinners.arc  # spinner type
224        sp.text = "Arc spinner"    # text along with spinner
225        sp.color = "green"         # spinner color
226        sp.side = "right"          # put spinner to the right
227        sp.reversal = True         # reverse spin direction
228
229        time.sleep(2)
230
231
232Spinner with timer
233//////////////////
234
235.. code:: python
236
237    import time
238    from yaspin import yaspin
239
240    with yaspin(text="elapsed time", timer=True) as sp:
241        time.sleep(3.1415)
242        sp.ok()
243
244
245Dynamic text
246////////////
247
248.. code:: python
249
250    import time
251    from datetime import datetime
252    from yaspin import yaspin
253
254    class TimedText:
255        def __init__(self, text):
256            self.text = text
257            self._start = datetime.now()
258
259        def __str__(self):
260            now = datetime.now()
261            delta = now - self._start
262            return f"{self.text} ({round(delta.total_seconds(), 1)}s)"
263
264    with yaspin(text=TimedText("time passed:")):
265        time.sleep(3)
266
267
268Writing messages
269////////////////
270
271.. image:: https://raw.githubusercontent.com/pavdmyt/yaspin/master/gifs/write_text.gif
272
273You should not write any message in the terminal using ``print`` while spinner is open.
274To write messages in the terminal without any collision with ``yaspin`` spinner, a ``.write()`` method is provided:
275
276.. code:: python
277
278    import time
279    from yaspin import yaspin
280
281    with yaspin(text="Downloading images", color="cyan") as sp:
282        # task 1
283        time.sleep(1)
284        sp.write("> image 1 download complete")
285
286        # task 2
287        time.sleep(2)
288        sp.write("> image 2 download complete")
289
290        # finalize
291        sp.ok("✔")
292
293
294Integration with other libraries
295////////////////////////////////
296
297.. image:: https://raw.githubusercontent.com/pavdmyt/yaspin/master/gifs/hide_show.gif
298
299Utilizing ``hidden`` context manager it is possible to toggle the display of
300the spinner in order to call custom methods that write to the terminal. This is
301helpful for allowing easy usage in other frameworks like `prompt-toolkit`_.
302Using the powerful ``print_formatted_text`` function allows you even to apply
303HTML formats and CSS styles to the output:
304
305.. code:: python
306
307    import sys
308    import time
309
310    from yaspin import yaspin
311    from prompt_toolkit import HTML, print_formatted_text
312    from prompt_toolkit.styles import Style
313
314    # override print with feature-rich ``print_formatted_text`` from prompt_toolkit
315    print = print_formatted_text
316
317    # build a basic prompt_toolkit style for styling the HTML wrapped text
318    style = Style.from_dict({
319        'msg': '#4caf50 bold',
320        'sub-msg': '#616161 italic'
321    })
322
323
324    with yaspin(text='Downloading images') as sp:
325        # task 1
326        time.sleep(1)
327        with sp.hidden():
328            print(HTML(
329                u'<b>></b> <msg>image 1</msg> <sub-msg>download complete</sub-msg>'
330            ), style=style)
331
332        # task 2
333        time.sleep(2)
334        with sp.hidden():
335            print(HTML(
336                u'<b>></b> <msg>image 2</msg> <sub-msg>download complete</sub-msg>'
337            ), style=style)
338
339        # finalize
340        sp.ok()
341
342
343Handling POSIX `signals`_
344/////////////////////////
345
346Handling keyboard interrupts (pressing Control-C):
347
348.. code:: python
349
350    import time
351
352    from yaspin import kbi_safe_yaspin
353
354
355    with kbi_safe_yaspin(text="Press Control+C to send SIGINT (Keyboard Interrupt) signal"):
356        time.sleep(5)  # time consuming code
357
358
359Handling other types of signals:
360
361.. code:: python
362
363    import os
364    import time
365    from signal import SIGTERM, SIGUSR1
366
367    from yaspin import yaspin
368    from yaspin.signal_handlers import default_handler, fancy_handler
369
370
371    sigmap = {SIGUSR1: default_handler, SIGTERM: fancy_handler}
372    with yaspin(sigmap=sigmap, text="Handling SIGUSR1 and SIGTERM signals") as sp:
373        sp.write("Send signals using `kill` command")
374        sp.write("E.g. $ kill -USR1 {0}".format(os.getpid()))
375        time.sleep(20)  # time consuming code
376
377
378More `examples`_.
379
380
381Development
382-----------
383
384Clone the repository:
385
386.. code-block:: bash
387
388    git clone https://github.com/pavdmyt/yaspin.git
389
390
391Install dev dependencies:
392
393.. code-block:: bash
394
395    poetry install
396
397    # if you don't have poetry installed:
398    pip install -r requirements.txt
399
400
401Lint code:
402
403.. code-block:: bash
404
405    make lint
406
407
408Format code:
409
410.. code-block:: bash
411
412    make black-fmt
413
414
415Run tests:
416
417.. code-block:: bash
418
419    make test
420
421
422Contributing
423------------
424
4251. Fork it!
4262. Create your feature branch: ``git checkout -b my-new-feature``
4273. Commit your changes: ``git commit -m 'Add some feature'``
4284. Push to the branch: ``git push origin my-new-feature``
4295. Submit a pull request
4306. Make sure tests are passing
431
432
433License
434-------
435
436* MIT - Pavlo Dmytrenko; https://twitter.com/pavdmyt
437* Contains data from `cli-spinners`_: MIT License, Copyright (c) Sindre Sorhus sindresorhus@gmail.com (sindresorhus.com)
438
439
440.. |Logo| image:: https://raw.githubusercontent.com/pavdmyt/yaspin/master/static/logo_80.png
441   :alt: yaspin Logo
442.. |Build Status| image:: https://travis-ci.org/pavdmyt/yaspin.svg?branch=master
443   :target: https://travis-ci.org/pavdmyt/yaspin
444.. |Coverage| image:: https://codecov.io/gh/pavdmyt/yaspin/branch/master/graph/badge.svg
445   :target: https://codecov.io/gh/pavdmyt/yaspin
446.. |pypi| image:: https://img.shields.io/pypi/v/yaspin.svg
447   :target: https://pypi.org/project/yaspin/
448.. |Versions| image:: https://img.shields.io/pypi/pyversions/yaspin.svg
449   :target: https://pypi.org/project/yaspin/
450.. |Wheel| image:: https://img.shields.io/pypi/wheel/yaspin.svg
451   :target: https://pypi.org/project/yaspin/
452.. |Examples| image:: https://img.shields.io/badge/learn%20by-examples-0077b3.svg
453   :target: https://github.com/pavdmyt/yaspin/tree/master/examples
454.. |pyup| image:: https://pyup.io/repos/github/pavdmyt/yaspin/shield.svg
455   :target: https://pyup.io/repos/github/pavdmyt/yaspin/
456.. |black-fmt| image:: https://img.shields.io/badge/code%20style-black-000000.svg
457   :target: https://github.com/ambv/black
458.. |DownloadsTot| image:: https://pepy.tech/badge/yaspin
459   :target: https://pepy.tech/project/yaspin
460.. |DownloadsW| image:: https://pepy.tech/badge/yaspin/week
461   :target: https://pepy.tech/project/yaspin
462
463
464.. _context manager: https://docs.python.org/3/reference/datamodel.html#context-managers
465.. _decorator: https://www.thecodeship.com/patterns/guide-to-python-function-decorators/
466.. _cli-spinners: https://github.com/sindresorhus/cli-spinners
467.. _termcolor: https://pypi.org/project/termcolor/
468.. _PyPI: https://pypi.org/
469.. _��: https://en.wikipedia.org/wiki/Any_Colour_You_Like
470.. _examples: https://github.com/pavdmyt/yaspin/tree/master/examples
471.. _prompt-toolkit: https://github.com/jonathanslenders/python-prompt-toolkit/
472.. _signals: https://www.computerhope.com/unix/signals.htm
473