Pythonic module for representing and manipulating file sizes with different prefix notations (file size unit conversion)
Project description
bitmath
bitmath simplifies many facets of interacting with file sizes in various units. Originally focusing on file size unit conversion, functionality now includes:
Converting between SI and NIST prefix units (kB to GiB)
Converting between units of the same type (SI to SI, or NIST to NIST)
Full NIST unit coverage including ZiB, YiB, Zib, and Yib
Automatic human-readable prefix selection (like in hurry.filesize)
Basic arithmetic operations (subtracting 42KiB from 50GiB)
Capacity math with floor division, modulo, and divmod (GiB(1) // MiB(300), GiB(1) % MiB(300))
Rich comparison operations (1024 Bytes == 1KiB)
Bitwise operations (<<, >>, &, |, ^)
Rounding via math.floor, math.ceil, and round
Reading a device’s storage capacity (Linux/macOS support only)
String parsing, including flexible non-strict parsing of ambiguous input
Sorting
Summing iterables via built-in sum or bitmath.sum for unit-normalised results
f-string and format support via the standard Python formatting protocol
argparse integration as a custom type
In addition to the conversion and math operations, bitmath provides human readable representations of values which are suitable for use in interactive shells as well as larger scripts and applications. The format produced for these representations is customizable via the functionality included in stdlibs string.format.
In discussion we will refer to the NIST units primarily. I.e., instead of “megabyte” we will refer to “mebibyte”. The former is 10^3 = 1,000,000 bytes, whereas the second is 2^20 = 1,048,576 bytes. When you see file sizes or transfer rates in your web browser, most of the time what you’re really seeing are the base-2 sizes/rates.
Don’t Forget! The source for bitmath is available on GitHub.
And did we mention there are nearly 300 unit tests? Check them out for yourself.
Running the tests should be as simple as calling the ci target in the Makefile: make ci. Please file a bug report if you run into issues.
Installation
The easiest way to install bitmath is via dnf (or yum) if you’re on a Fedora/RHEL based distribution. bitmath is available in the main Fedora repositories, as well as EPEL Repositories. As of 2023 bitmath is only developed, tested, and supported for currently supported Python releases.
$ sudo dnf install python3-bitmath
PyPI:
You could also install bitmath from PyPI if you like:
$ pip install --user bitmath
Source:
To install from source, clone the repository and use pip:
$ git clone https://github.com/timlnx/bitmath.git
$ cd bitmath
$ pip install .
To also install the bitmath manpage:
$ sudo make install
Documentation
The main documentation lives at http://bitmath.readthedocs.org/en/latest/.
Topics include:
The bitmath Module
Utility Functions
Context Managers
Module Variables
argparse integration
The bitmath command-line Tool
Classes
Initializing
Available Classes
Class Methods
Instances
Instance Attributes
Instance Methods
Instance Properties
The Formatting Mini-Language
Getting Started
Tables of Supported Operations
Basic Math
Unit Conversion
Rich Comparison
Sorting
Real Life Examples
Download Speeds
Calculating how many files fit on a device
Printing Human-Readable File Sizes in Python
Calculating Linux BDP and TCP Window Scaling
Contributing to bitmath
Appendices
Rules for Math
On Units
Who uses Bitmath
Related Projects
NEWS
Copyright
Examples
Arithmetic
>>> import bitmath
>>> log_size = bitmath.kB(137.4)
>>> log_zipped_size = bitmath.Byte(987)
>>> print("Compression saved %s space" % (log_size - log_zipped_size))
Compression saved 136.413kB space
>>> thumb_drive = bitmath.GiB(12)
>>> song_size = bitmath.MiB(5)
>>> songs_per_drive = thumb_drive / song_size
>>> print(songs_per_drive)
2457.6
Capacity Planning
Floor division (//), modulo (%), and divmod() are handy for chunk-and-remainder capacity math. bm1 // bm2 returns an int (how many whole chunks fit); bm1 % bm2 returns a bitmath of the left-hand operand’s type (the leftover).
>>> from bitmath import GiB, MiB, TiB
>>> disk = GiB(1)
>>> chunk = MiB(300)
>>> disk // chunk # how many whole 300 MiB chunks fit?
3
>>> disk % chunk # leftover, typed as the LHS (GiB)
GiB(0.12109375)
>>> divmod(disk, chunk) # both at once
(3, GiB(0.12109375))
Re-express the remainder in a human-readable unit with best_prefix() (or coerce directly with to_MiB(), etc.):
>>> (GiB(1) % MiB(300)).best_prefix()
MiB(124.0)
Pair with the bitmath.format context manager for clean reporting across a block of capacity calculations:
>>> import bitmath
>>> volume = TiB(1)
>>> block = GiB(7)
>>> with bitmath.format(fmt_str="{value:.2f} {unit}", bestprefix=True):
... whole, leftover = divmod(volume, block)
... print(f"{whole} whole blocks of {block} fit in {volume}")
... print(f"leftover: {leftover}")
146 whole blocks of 7.00 GiB fit in 1.00 TiB
leftover: 2.00 GiB
The identity (a // b) * b + (a % b) == a holds, so divmod round-trips.
Convert Units
File size unit conversion:
>>> from bitmath import *
>>> dvd_size = GiB(4.7)
>>> print("DVD Size in MiB: %s" % dvd_size.to_MiB())
DVD Size in MiB: 4812.8 MiB
Select a human-readable unit
>>> small_number = kB(100)
>>> ugly_number = small_number.to_TiB()
>>> print(ugly_number)
9.09494701773e-08 TiB
>>> print(ugly_number.best_prefix())
97.65625 KiB
Rich Comparison
>>> cd_size = MiB(700)
>>> cd_size > dvd_size
False
>>> cd_size < dvd_size
True
>>> MiB(1) == KiB(1024)
True
>>> MiB(1) <= KiB(1024)
True
Sorting
>>> sizes = [KiB(7337.0), KiB(1441.0), KiB(2126.0), KiB(2178.0),
KiB(2326.0), KiB(4003.0), KiB(48.0), KiB(1770.0),
KiB(7892.0), KiB(4190.0)]
>>> print(sorted(sizes))
[KiB(48.0), KiB(1441.0), KiB(1770.0), KiB(2126.0), KiB(2178.0),
KiB(2326.0), KiB(4003.0), KiB(4190.0), KiB(7337.0), KiB(7892.0)]
Custom Formatting
Use of the custom formatting system
All of the available instance properties
Example:
>>> longer_format = """Formatting attributes for %s
...: This instances prefix unit is {unit}, which is a {system} type unit
...: The unit value is {value}
...: This value can be truncated to just 1 digit of precision: {value:.1f}
...: In binary this looks like: {binary}
...: The prefix unit is derived from a base of {base}
...: Which is raised to the power {power}
...: There are {bytes} bytes in this instance
...: The instance is {bits} bits large
...: bytes/bits without trailing decimals: {bytes:.0f}/{bits:.0f}""" % str(ugly_number)
>>> print(ugly_number.format(longer_format))
Formatting attributes for 5.96046447754 MiB
This instances prefix unit is MiB, which is a NIST type unit
The unit value is 5.96046447754
This value can be truncated to just 1 digit of precision: 6.0
In binary this looks like: 0b10111110101111000010000000
The prefix unit is derived from a base of 2
Which is raised to the power 20
There are 6250000.0 bytes in this instance
The instance is 50000000.0 bits large
bytes/bits without trailing decimals: 6250000/50000000
Utility Functions
bitmath.getsize()
>>> print(bitmath.getsize('python-bitmath.spec'))
3.7060546875 KiB
bitmath.parse_string()
Parse a string with standard units:
>>> import bitmath
>>> a_dvd = bitmath.parse_string("4.7 GiB")
>>> print(type(a_dvd))
<class 'bitmath.GiB'>
>>> print(a_dvd)
4.7 GiB
bitmath.parse_string_unsafe()
Parse a string with ambiguous units:
>>> import bitmath
>>> a_gig = bitmath.parse_string_unsafe("1gb")
>>> print(type(a_gig))
<class 'bitmath.GB'>
>>> a_gig == bitmath.GB(1)
True
>>> bitmath.parse_string_unsafe('1gb') == bitmath.parse_string_unsafe('1g')
True
bitmath.query_device_capacity()
>>> import bitmath
>>> with open('/dev/sda') as fp:
... root_disk = bitmath.query_device_capacity(fp)
... print(root_disk.best_prefix())
...
238.474937439 GiB
bitmath.listdir()
>>> for i in bitmath.listdir('./tests/', followlinks=True, relpath=True, bestprefix=True):
... print(i)
...
('tests/test_file_size.py', KiB(9.2900390625))
('tests/test_basic_math.py', KiB(7.1767578125))
('tests/__init__.py', KiB(1.974609375))
('tests/test_bitwise_operations.py', KiB(2.6376953125))
('tests/test_context_manager.py', KiB(3.7744140625))
('tests/test_representation.py', KiB(5.2568359375))
('tests/test_properties.py', KiB(2.03125))
('tests/test_instantiating.py', KiB(3.4580078125))
('tests/test_future_math.py', KiB(2.2001953125))
('tests/test_best_prefix_BASE.py', KiB(2.1044921875))
('tests/test_rich_comparison.py', KiB(3.9423828125))
('tests/test_best_prefix_NIST.py', KiB(5.431640625))
('tests/test_unique_testcase_names.sh', Byte(311.0))
('tests/.coverage', KiB(3.1708984375))
('tests/test_best_prefix_SI.py', KiB(5.34375))
('tests/test_to_built_in_conversion.py', KiB(1.798828125))
('tests/test_to_Type_conversion.py', KiB(8.0185546875))
('tests/test_sorting.py', KiB(4.2197265625))
('tests/listdir_symlinks/10_byte_file_link', Byte(10.0))
('tests/listdir_symlinks/depth1/depth2/10_byte_file', Byte(10.0))
('tests/listdir_nosymlinks/depth1/depth2/10_byte_file', Byte(10.0))
('tests/listdir_nosymlinks/depth1/depth2/1024_byte_file', KiB(1.0))
('tests/file_sizes/kbytes.test', KiB(1.0))
('tests/file_sizes/bytes.test', Byte(38.0))
('tests/listdir/10_byte_file', Byte(10.0))
Formatting
>>> with bitmath.format(fmt_str="[{value:.3f}@{unit}]"):
... for i in bitmath.listdir('./tests/', followlinks=True, relpath=True, bestprefix=True):
... print(i[1])
...
[9.290@KiB]
[7.177@KiB]
[1.975@KiB]
[2.638@KiB]
[3.774@KiB]
[5.257@KiB]
[2.031@KiB]
[3.458@KiB]
[2.200@KiB]
[2.104@KiB]
[3.942@KiB]
[5.432@KiB]
[311.000@Byte]
[3.171@KiB]
[5.344@KiB]
[1.799@KiB]
[8.019@KiB]
[4.220@KiB]
[10.000@Byte]
[10.000@Byte]
[10.000@Byte]
[1.000@KiB]
[1.000@KiB]
[38.000@Byte]
[10.000@Byte]
argparse Integration
A self-contained example showing how to use bitmath as an argparse argument type is available in the Integration Examples chapter of the documentation.
import argparse
import bitmath
def BitmathType(value):
try:
return bitmath.parse_string(value)
except ValueError:
raise argparse.ArgumentTypeError(
f"{value!r} is not a recognized bitmath unit string"
)
parser = argparse.ArgumentParser()
parser.add_argument('--block-size', type=BitmathType, required=True)
args = parser.parse_args(['--block-size', '10MiB'])
print(args.block_size) # 10.0 MiB
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file bitmath-2.0.1.tar.gz.
File metadata
- Download URL: bitmath-2.0.1.tar.gz
- Upload date:
- Size: 103.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ebc87550d662f7891a481b53f25f6259dbd279bbdc85cf926202e1b7548f3dbd
|
|
| MD5 |
4fe96a3857fc06fd018eee6f10e7098d
|
|
| BLAKE2b-256 |
ac9849c85f55d00ff24c03c13ef171c5a3b8aea68ab8cd0bb1ecb235e2f45d3b
|
Provenance
The following attestation bundles were made for bitmath-2.0.1.tar.gz:
Publisher:
publish.yml on timlnx/bitmath
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
bitmath-2.0.1.tar.gz -
Subject digest:
ebc87550d662f7891a481b53f25f6259dbd279bbdc85cf926202e1b7548f3dbd - Sigstore transparency entry: 1395819469
- Sigstore integration time:
-
Permalink:
timlnx/bitmath@b5531bb2d1458a30e31cf729cb8409717a90b10e -
Branch / Tag:
refs/tags/v2.0.1 - Owner: https://github.com/timlnx
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b5531bb2d1458a30e31cf729cb8409717a90b10e -
Trigger Event:
release
-
Statement type:
File details
Details for the file bitmath-2.0.1-py3-none-any.whl.
File metadata
- Download URL: bitmath-2.0.1-py3-none-any.whl
- Upload date:
- Size: 24.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f7d6c1160f441429f01c2102158f3d029366c25f4908e3f833bd942dda8a14db
|
|
| MD5 |
a302d745aec8aea3e1af0d58986afab9
|
|
| BLAKE2b-256 |
010f441e365ec02653284159155357cba93b4680638cf78f74fed0b954af498f
|
Provenance
The following attestation bundles were made for bitmath-2.0.1-py3-none-any.whl:
Publisher:
publish.yml on timlnx/bitmath
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
bitmath-2.0.1-py3-none-any.whl -
Subject digest:
f7d6c1160f441429f01c2102158f3d029366c25f4908e3f833bd942dda8a14db - Sigstore transparency entry: 1395819473
- Sigstore integration time:
-
Permalink:
timlnx/bitmath@b5531bb2d1458a30e31cf729cb8409717a90b10e -
Branch / Tag:
refs/tags/v2.0.1 - Owner: https://github.com/timlnx
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@b5531bb2d1458a30e31cf729cb8409717a90b10e -
Trigger Event:
release
-
Statement type: