7. Integration Examples
The following are self-contained, copy-paste examples showing how to use
bitmath with popular third-party libraries. These libraries are
not installed by bitmath — install them separately before use.
7.1. argparse
The argparse module (part of the Python standard library) accepts
command-line arguments as strings by default. The type parameter of
add_argument() lets you supply a
callable that converts a raw string into whatever type your application
needs.
The snippet below defines a BitmathType callable and registers it as
the type for a --block-size option so that users can write values
like --block-size 10MiB and receive a bitmath.MiB object
directly.
import argparse
import bitmath
def BitmathType(value):
"""Convert a command-line string such as '10MiB' into a bitmath object."""
try:
return bitmath.parse_string(value)
except ValueError:
raise argparse.ArgumentTypeError(
f"{value!r} is not a recognized bitmath unit string "
"(examples: 10MiB, 1.5GiB, 500kB)"
)
def main():
parser = argparse.ArgumentParser(
description="Example script using a bitmath argument type"
)
parser.add_argument(
"--block-size",
type=BitmathType,
required=True,
help="Block size with unit, e.g. 10MiB",
)
args = parser.parse_args()
print(f"Block size: {args.block_size}")
print(f"In KiB: {args.block_size.to_KiB():.2f}")
if __name__ == "__main__":
main()
Example run:
$ python script.py --block-size 10MiB
Block size: 10.0 MiB
In KiB: 10240.00 KiB
$ python script.py --block-size bad
error: argument --block-size: 'bad' is not a recognized bitmath unit string (examples: 10MiB, 1.5GiB, 500kB)
7.1.1. argparse validation
Now say you want to perform some additional validation on this custom
BitmathType unit. This is best done after the parsing is complete, not
as part of the BitmathType implementation. This follows the advice outlined
in the upstream argparse documentation.
In general, the
typekeyword is a convenience that should only be used for simple conversions that can only raise one of the three supported exceptions. Anything with more interesting error-handling or resource management should be done downstream after the arguments are parsed.
First, parse the unit, allow BitmathType to handle the parsing validation.
Second, perform your own context-aware validation. For example, you might set
minimum or maximums and need to compare the parsed argument against them.
1import argparse
2import bitmath
3
4
5def BitmathType(value):
6 """Convert a command-line string such as '10MiB' into a bitmath object."""
7 try:
8 return bitmath.parse_string(value)
9 except ValueError:
10 raise argparse.ArgumentTypeError(
11 f"{value!r} is not a recognized bitmath unit string "
12 "(examples: 10MiB, 1.5GiB, 500kB)"
13 )
14
15
16def main():
17 max_block_size = bitmath.GiB(1)
18 parser = argparse.ArgumentParser(
19 description="Example script using a bitmath argument type"
20 )
21 parser.add_argument(
22 "--block-size",
23 type=BitmathType,
24 required=True,
25 help="Block size with unit, e.g. 10MiB",
26 )
27 args = parser.parse_args()
28
29 if args.block_size > bitmath.GiB(1):
30 raise ValueError(f"Provided block size {args.block_size} exceeds maximum {max_block_size}")
31
32 print(f"Block size: {args.block_size}")
33 print(f"In KiB: {args.block_size.to_KiB():.2f}")
34
35
36if __name__ == "__main__":
37 main()
Example run:
$ python script.py --block-size 42GiB
Traceback (most recent call last):
File "script.py", line 37, in <module>
main()
~~~~^^
File "script.py", line 30, in main
raise ValueError(f"Provided block size {args.block_size} exceeds maximum {max_block_size}")
ValueError: Provided block size 42.0 GiB exceeds maximum 1.0 GiB
7.2. click
click is a popular command-line
interface toolkit. Custom parameter types are implemented by subclassing
click.ParamType and overriding convert().
Install click before use:
pip install click
import click
import bitmath
class BitmathParamType(click.ParamType):
"""A click parameter type that accepts bitmath unit strings."""
name = "SIZE"
def convert(self, value, param, ctx):
if isinstance(value, bitmath.Bitmath):
return value
try:
return bitmath.parse_string(value)
except ValueError:
self.fail(
f"{value!r} is not a recognized bitmath unit string "
"(examples: 10MiB, 1.5GiB, 500kB)",
param,
ctx,
)
BITMATH = BitmathParamType()
@click.command()
@click.option(
"--block-size",
type=BITMATH,
required=True,
help="Block size with unit, e.g. 10MiB",
)
def main(block_size):
"""Example command using a bitmath click parameter type."""
click.echo(f"Block size: {block_size}")
click.echo(f"In KiB: {block_size.to_KiB():.2f}")
if __name__ == "__main__":
main()
Example run:
$ python script.py --block-size 10MiB
Block size: 10.0 MiB
In KiB: 10240.00 KiB
$ python script.py --block-size bad
Error: Invalid value for '--block-size': 'bad' is not a recognized bitmath unit string (examples: 10MiB, 1.5GiB, 500kB)
7.3. progressbar2
progressbar2 is a flexible terminal progress-bar library. The example below defines a custom widget that displays a data-transfer speed (bytes per second) in a human-readable bitmath unit, and demonstrates it with a simulated file download.
Install progressbar2 before use:
pip install progressbar2
import time
import progressbar
import bitmath
class DataTransferSpeed(progressbar.widgets.FormatWidgetMixin,
progressbar.widgets.TimeSensitiveMixin):
"""Display transfer speed as a human-readable bitmath value per second."""
def __call__(self, progress, data, **kwargs):
elapsed = data.get("seconds_elapsed") or 0
if elapsed <= 0 or data.get("value") is None:
return "?? B/s"
bytes_done = data["value"]
speed = bitmath.Byte(bytes_done / elapsed).best_prefix()
return f"{speed:.2f}/s"
def simulate_download(total_bytes):
widgets = [
"Downloading: ",
progressbar.Bar(),
" ",
progressbar.Percentage(),
" ",
DataTransferSpeed(),
" ",
progressbar.ETA(),
]
with progressbar.ProgressBar(
max_value=total_bytes, widgets=widgets
) as bar:
received = 0
chunk = total_bytes // 50
while received < total_bytes:
time.sleep(0.05)
received = min(received + chunk, total_bytes)
bar.update(received)
if __name__ == "__main__":
# Simulate a 100 MiB download
simulate_download(int(bitmath.MiB(100).to_Byte()))
Example run:
Downloading: |####################| 100% 18.32 MiB/s ETA: 0:00:00