ENLIGHTEN(1) | Enlighten | ENLIGHTEN(1) |
enlighten - Enlighten Documentation
$ pip install enlighten
(EPEL repositories must be configured for EL8)
$ dnf install python3-enlighten
(EPEL repositories must be configured)
$ yum install python2-enlighten $ yum install python36-enlighten
$ pacman -S python-enlighten
$ apt-get install python3-enlighten
$ conda install -c conda-forge enlighten
For a basic status bar, invoke the Counter class directly.
import time import enlighten pbar = enlighten.Counter(total=100, desc='Basic', unit='ticks') for num in range(100):
time.sleep(0.1) # Simulate work
pbar.update()
To maintain multiple progress bars simultaneously or write to the console, a manager is required.
Advanced output will only work when the output stream, sys.__stdout__ by default, is attached to a TTY. get_manager() can be used to get a manager instance. It will return a disabled Manager instance if the stream is not attached to a TTY and an enabled instance if it is.
import time import enlighten manager = enlighten.get_manager() ticks = manager.counter(total=100, desc='Ticks', unit='ticks') tocks = manager.counter(total=20, desc='Tocks', unit='tocks') for num in range(100):
time.sleep(0.1) # Simulate work
print(num)
ticks.update()
if not num % 5:
tocks.update() manager.stop()
The Counter class has two output formats, progress bar and counter.
The progress bar format is used when a total is not None and the count is less than the total. If neither of these conditions are met, the counter format is used:
import time import enlighten counter = enlighten.Counter(desc='Basic', unit='ticks') for num in range(100):
time.sleep(0.1) # Simulate work
counter.update()
Status bars are bars that work similarly to progress similarly to progress bars and counters, but present relatively static information. Status bars are created with Manager.status_bar.
import enlighten import time manager = enlighten.get_manager() status_bar = manager.status_bar('Static Message',
color='white_on_red',
justify=enlighten.Justify.CENTER) time.sleep(1) status_bar.update('Updated static message') time.sleep(1)
Status bars can also use formatting with dynamic variables.
import enlighten import time manager = enlighten.get_manager() status_format = '{program}{fill}Stage: {stage}{fill} Status {status}' status_bar = manager.status_bar(status_format=status_format,
color='bold_slategray',
program='Demo',
stage='Loading',
status='OKAY') time.sleep(1) status_bar.update(stage='Initializing', status='OKAY') time.sleep(1) status_bar.update(status='FAIL')
Status bars, like other bars can be pinned. To pin a status bar to the top of all other bars, initialize it before any other bars. To pin a bar to the bottom of the screen, use position=1 when initializing.
See StatusBar for more details.
Status bars and the bar component of a progress bar can be colored by setting the color keyword argument. See Series Color for more information about valid colors.
import time import enlighten counter = enlighten.Counter(total=100, desc='Colorized', unit='ticks', color='red') for num in range(100):
time.sleep(0.1) # Simulate work counter.update()
Additionally, any part of the progress bar can be colored using counter formatting and the color capabilities of the underlying Blessed Terminal.
import enlighten manager = enlighten.get_manager() # Standard bar format std_bar_format = u'{desc}{desc_pad}{percentage:3.0f}%|{bar}| ' + \
u'{count:{len_total}d}/{total:d} ' + \
u'[{elapsed}<{eta}, {rate:.2f}{unit_pad}{unit}/s]' # Red text bar_format = manager.term.red(std_bar_format) # Red on white background bar_format = manager.term.red_on_white(std_bar_format) # X11 colors bar_format = manager.term.peru_on_seagreen(std_bar_format) # RBG text bar_format = manager.term.color_rgb(2, 5, 128)(std_bar_format) # RBG background bar_format = manager.term.on_color_rgb(255, 190, 195)(std_bar_format) # RGB text and background bar_format = manager.term.on_color_rgb(255, 190, 195)(std_bar_format) bar_format = manager.term.color_rgb(2, 5, 128)(bar_format) # Apply color to select parts bar_format = manager.term.red(u'{desc}') + u'{desc_pad}' + \
manager.term.blue(u'{percentage:3.0f}%') + u'|{bar}|' # Apply to counter ticks = manager.counter(total=100, desc='Ticks', unit='ticks', bar_format=bar_format)
If the color option is applied to a Counter, it will override any foreground color applied.
The bar component of a progress bar can be multicolored to track multiple categories in a single progress bar.
The colors are drawn from right to left in the order they were added.
By default, when multicolored progress bars are used, additional fields are available for bar_format:
When add_subcounter() is called with all_fields set to True, the subcounter will have the additional fields:
More information about bar_format can be found in the Format section of the API.
One use case for multicolored progress bars is recording the status of a series of tests. In this example, Failures are red, errors are white, and successes are green. The count of each is listed in the progress bar.
import random import time import enlighten bar_format = u'{desc}{desc_pad}{percentage:3.0f}%|{bar}| ' + \
u'S:{count_0:{len_total}d} ' + \
u'F:{count_2:{len_total}d} ' + \
u'E:{count_1:{len_total}d} ' + \
u'[{elapsed}<{eta}, {rate:.2f}{unit_pad}{unit}/s]' success = enlighten.Counter(total=100, desc='Testing', unit='tests',
color='green', bar_format=bar_format) errors = success.add_subcounter('white') failures = success.add_subcounter('red') while success.count < 100:
time.sleep(random.uniform(0.1, 0.3)) # Random processing time
result = random.randint(0, 10)
if result == 7:
errors.update()
if result in (5, 6):
failures.update()
else:
success.update()
A more complicated example is recording process start-up. In this case, all items will start red, transition to yellow, and eventually all will be green. The count, percentage, rate, and eta fields are all derived from the second subcounter added.
import random import time import enlighten services = 100 bar_format = u'{desc}{desc_pad}{percentage_2:3.0f}%|{bar}|' + \
u' {count_2:{len_total}d}/{total:d} ' + \
u'[{elapsed}<{eta_2}, {rate_2:.2f}{unit_pad}{unit}/s]' initializing = enlighten.Counter(total=services, desc='Starting', unit='services',
color='red', bar_format=bar_format) starting = initializing.add_subcounter('yellow') started = initializing.add_subcounter('green', all_fields=True) while started.count < services:
remaining = services - initializing.count
if remaining:
num = random.randint(0, min(4, remaining))
initializing.update(num)
ready = initializing.count - initializing.subcount
if ready:
num = random.randint(0, min(3, ready))
starting.update_from(initializing, num)
if starting.count:
num = random.randint(0, min(2, starting.count))
started.update_from(starting, num)
time.sleep(random.uniform(0.1, 0.5)) # Random processing time
Enlighten is highly configurable. For information on modifying the output, see the Series and Format sections of the Counter documentation.
A program may want to disable progress bars based on a configuration setting as well as if output redirection occurs.
import sys import enlighten # Example configuration object config = {'stream': sys.stdout,
'useCounter': False} enableCounter = config['useCounter'] and stream.isatty() manager = enlighten.Manager(stream=config['stream'], enabled=enableCounter)
The get_manager() function slightly simplifies this
import enlighten # Example configuration object config = {'stream': None, # Defaults to sys.__stdout__
'useCounter': False} manager = enlighten.get_manager(stream=config['stream'], enabled=config['useCounter'])
Both Counter and Manager can be used as context managers.
import enlighten SPLINES = 100 with enlighten.Manager() as manager:
with manager.counter(total=SPLINES, desc='Reticulating:', unit='splines') as retic:
for num in range(SPLINES + 1):
retic.update()
Both Counter and SubCounter instances can be called as functions on one or more iterators. A generator is returned which yields each element of the iterables and then updates the count by 1.
NOTE:
import time import enlighten flock1 = ['Harry', 'Sally', 'Randy', 'Mandy', 'Danny', 'Joe'] flock2 = ['Punchy', 'Kicky', 'Spotty', 'Touchy', 'Brenda'] total = len(flock1) + len(flock2) manager = enlighten.Manager() pbar = manager.counter(total=total, desc='Counting Sheep', unit='sheep') for sheep in pbar(flock1, flock2):
time.sleep(0.2)
print('%s: Baaa' % sheep)
Both Counter and StatusBar accept user defined fields as keyword arguments at initialization and during an update. These fields are persistent and only need to be specified when they change.
In the following example, source is a user-defined field that is periodically updated.
import enlighten import random import time bar_format = u'{desc}{desc_pad}{source} {percentage:3.0f}%|{bar}| ' + \
u'{count:{len_total}d}/{total:d} ' + \
u'[{elapsed}<{eta}, {rate:.2f}{unit_pad}{unit}/s]' manager = enlighten.get_manager(bar_format=bar_format) bar = manager.counter(total=100, desc='Loading', unit='files', source='server.a') for num in range(100):
time.sleep(0.1) # Simulate work
if not num % 5:
bar.update(source=random.choice(['server.a', 'server.b', 'server.c']))
else:
bar.update()
For more information, see the Counter Format and StatusBar Format sections.
Enlighten supports automatic SI (metric) and IEC (binary) prefixes using the Prefixed library.
All rate and interval formatting fields are of the type prefixed.Float. total and all count fields default to int. If total or or count are set to a float, or a float is provided to update(), these fields will be prefixed.Float instead.
import time import random import enlighten size = random.uniform(1.0, 10.0) * 2 ** 20 # 1-10 MiB (float) chunk_size = 64 * 1024 # 64 KiB bar_format = '{desc}{desc_pad}{percentage:3.0f}%|{bar}| ' \
'{count:!.2j}{unit} / {total:!.2j}{unit} ' \
'[{elapsed}<{eta}, {rate:!.2j}{unit}/s]' manager = enlighten.get_manager() pbar = manager.counter(total=size, desc='Downloading', unit='B', bar_format=bar_format) bytes_left = size while bytes_left:
time.sleep(random.uniform(0.05, 0.15))
next_chunk = min(chunk_size, bytes_left)
pbar.update(next_chunk)
bytes_left -= next_chunk
import enlighten counter_format = 'Trying to get to sleep: {count:.2h} sheep' counter = enlighten.Counter(counter_format=counter_format) counter.count = 0.0 for num in range(10000000):
counter.update()
For more information, see the Counter Format and the Prefixed documentation.
A progress bar's purpose is to inform the user about an ongoing process. Enlighten, meaning "to inform", seems a fitting name. (Plus any names related to progress were already taken)
Enlighten has supported Windows since version 1.3.0.
Windows does not currently support resizing.
Enlighten also works relatively well in Linux-like subsystems for Windows such as Cygwin or Windows Subsystem for Linux.
Experimental support for Jupyter notebooks was added in version 1.10.0.
Jupyter Notebook support is provide by the NotebookManager class. If running inside a Jupyter Notebook, get_manager() will return a NotebookManager instance.
There is currently no support for detecting the width of a Jupyter notebook so output width has been set statically to 100 characters. This can be overridden by passing the width keyword argument to get_manager().
PyCharm uses multiple consoles and the behavior differs depending on how the code is called.
Enlighten works natively in the PyCharm command terminal.
To use Enlighten with Run or Debug, terminal emulation must be enabled. Navigate to Run -> Edit Configurations -> Templates -> Python and select Emulate terminal in output console.
The PyCharm Python console is currently not supported because sys.stdout does not reference a valid TTY.
We are happy to add support for as many terminals as we can. However, not all terminals can be supported. There a few requirements.
This is caused when another thread or process is writing to a standard stream (STDOUT, STDERR) at the same time the resize signal handler is writing to the stream.
Enlighten tries to detect when a program is threaded or running multiple processes and defer resize handling until the next normal write event. However, this condition is evaluated when the scroll area is set, typically when the first counter is added. If no threads or processes are detected at that time, and the value of threaded was not set explicitly, resize events will not be deferred.
In order to guarantee resize handling is deferred, it is best to pass threaded=True when creating a manager instance.
This is caused by a bug in the VSCode Jupyter extension that reverts display updates at the end of a cell. This issue has been confirmed by the maintainers, but a fix has not been released yet.
As a temporary workaround, manager._primed = False at the end of the cell before calling stop(). Note, this will result in double output in other environments so should only be used if the display is cleared first or the code is limited to running in this extension.
Manager class for outputting progress bars to streams attached to TTYs
Progress bars are displayed at the bottom of the screen with standard output displayed above.
companion_stream
If the value is None, the companion stream will be dynamically determined. Unless explicitly specified, a stream which is not attached to a TTY (the case when redirected to a file), will not be used as a companion stream.
Get a new progress bar instance
If position is specified, the counter's position will be pinned. A ValueError will be raised if position exceeds the screen height or has already been pinned by another counter.
If autorefresh is True, this bar will be redrawn whenever another bar is drawn assuming it had been min_delta seconds since the last update. This is usually unnecessary.
NOTE:
Get a new status bar instance
If position is specified, the counter's position can change dynamically if additional counters are called without a position argument.
If autorefresh is True, this bar will be redrawn whenever another bar is drawn assuming it had been min_delta seconds since the last update. Generally, only need when elapsed is used in status_format.
This method should be called when the manager and counters will no longer be needed.
Any progress bars that have leave set to True or have not been closed will remain on the console. All others will be cleared.
Manager and all counters will be disabled.
Manager class for outputting progress bars to Jupyter notebooks
The following keyword arguments are set if provided, but ignored:
Get a new progress bar instance
If position is specified, the counter's position will be pinned. A ValueError will be raised if position exceeds the screen height or has already been pinned by another counter.
If autorefresh is True, this bar will be redrawn whenever another bar is drawn assuming it had been min_delta seconds since the last update. This is usually unnecessary.
NOTE:
Get a new status bar instance
If position is specified, the counter's position can change dynamically if additional counters are called without a position argument.
If autorefresh is True, this bar will be redrawn whenever another bar is drawn assuming it had been min_delta seconds since the last update. Generally, only need when elapsed is used in status_format.
This method should be called when the manager and counters will no longer be needed.
Any progress bars that have leave set to True or have not been closed will remain on the console. All others will be cleared.
Manager and all counters will be disabled.
Progress bar and counter class
A Counter instance can be created with the Manager.counter() method or, when a standalone progress bar for simple applications is required, the Counter class can be called directly. The output stream will default to sys.__stdout__ unless stream is set.
NOTE:
Series
Default progress series (series):
' ▏▎▍▌▋▊▉█'
The first character is the fill character. When the count is 0, the bar will be made up of only this character. In the example below, characters 5 through 9 are fill characters.
The last character is the full character. When the count is equal to total, the bar will be made up of only this character. In the example below, characters 0 through 3 are full characters.
The remaining characters are fractional characters used to more accurately represent the transition between the full and fill characters. In the example below, character 4 is a fractional character.
'45% |████▋ |'
'0123456789'
Series Color
color can be specified as None, a string or, an iterable of three integers, 0 - 255, describing an RGB color.
For backward compatibility, a color can be expressed as an integer 0 - 255, but this is deprecated in favor of named or RGB colors.
Compound colors, such as 'white_on_seagreen', 'bold_red', or 'underline_on_peru' are also supported.
If a terminal is not capable of 24-bit color, and is given a color outside of its range, the color will be downconverted to a supported color.
Valid colors for 8 color terminals:
Additional colors for 16 color terminals:
See this chart for a complete list of supported color strings.
NOTE:
Format
Default counter format (counter_format):
'{desc}{desc_pad}{count:d} {unit}{unit_pad}{elapsed}, {rate:.2f}{unit_pad}{unit}/s]{fill}' # Example output 'Loaded 30042 Files [00:01, 21446.45 Files/s] '
Default progress bar format (bar_format):
'{desc}{desc_pad}{percentage:3.0f}%|{bar}| {count:{len_total}d}/{total:d} [{elapsed}<{eta}, {rate:.2f}{unit_pad}{unit}/s]' # Example output 'Processing 22%|█████▊ | 23/101 [00:27<01:32, 0.84 Files/s]'
Available fields:
Additional fields for bar_format only:
Additional fields for counter_format only:
Additional fields when subcounters are used:
NOTE:
Additional fields when add_subcounter() is called with all_fields set to True:
NOTE:
This allows additional format specifiers using SI (metric) and IEC (binary) prefixes. See the Prefixed documentation for more details.
This will also require a custom format and affect the accuracy of the len_total field.
User-defined fields:
The fields parameter can be used to pass a dictionary of additional user-defined fields. The dictionary values can be updated after initialization to allow for dynamic fields. Any fields that share names with built-in fields are ignored.
If fields are passed as keyword arguments to Manager.counter() or Counter.update(), they take precedent over the fields parameter.
Offset
Under special circumstances, and to permit backward compatibility, offset may be explicitly set to an int value. When explicitly set, automatic detection of escape sequences is disabled.
Instance Attributes
Add a subcounter for multicolored progress bars
If leave is True, the default, the effect is the same as refresh().
Preferred to be a string or iterable of three integers for RGB. Single integer supported for backwards compatibility
Format progress bar or counter
Redraw bar
Increment progress bar and redraw
Progress bar is only redrawn if min_delta seconds past since the last update
Status bar class
A StatusBar instance should be created with the Manager.status_bar() method.
Status Color
Color works similarly to color on Counter, except it affects the entire status bar. See Series Color for more information.
Format
There are two ways to populate the status bar, direct and formatted. Direct takes precedence over formatted.
Direct Status
Direct status is used when arguments are passed to Manager.status_bar() or StatusBar.update(). Any arguments are coerced to strings and joined with a space. For example:
status_bar.update('Hello', 'World!') # Example output: Hello World! status_bar.update('Hello World!') # Example output: Hello World! count = [1, 2, 3, 4] status_bar.update(*count)
# Example output: 1 2 3 4
Formatted Status
'Current Stage: {stage}' # Example output 'Current Stage: Testing'
Available fields:
NOTE:
User-defined fields:
The fields parameter can be used to pass a dictionary of additional user-defined fields. The dictionary values can be updated after initialization to allow for dynamic fields. Any fields that share names with available fields are ignored.
If fields are passed as keyword arguments to Manager.status_bar() or StatusBar.update(), they take precedent over the fields parameter.
Instance Attributes
If leave is True, the default, the effect is the same as refresh().
Preferred to be a string or iterable of three integers for RGB. Single integer supported for backwards compatibility
Format status bar
Redraw bar
Update status and redraw
Status bar is only redrawn if min_delta seconds past since the last update
This class tracks a portion of multicolored progress bar and should be initialized through Counter.add_subcounter()
Instance Attributes
Increment progress bar and redraw
Both this counter and the parent are incremented.
Progress bar is only redrawn if min_delta seconds past since the last update on the parent.
Move a value to this counter from another counter.
source must be the parent Counter instance or a SubCounter with the same parent
Convenience function to get a manager instance
If running inside a notebook, a NotebookManager instance is returned. otherwise a standard Manager instance is returned.
If a a standard Manager instance is used and stream is not attached to a TTY, the Manager instance is disabled.
Enlighten Progress Bar is a console progress bar library for Python.
The main advantage of Enlighten is it allows writing to stdout and stderr without any redirection or additional code. Just print or log as you normally would.
Enlighten also includes experimental support for Jupyter Notebooks.
The code for this animation can be found in demo.py in examples.
Avram Lubkin
2023 - 2022, Avram Lubkin
February 26, 2023 | 1.11.2 |