Metadata-Version: 2.4
Name: bitmath
Version: 2.0.1
Summary: Pythonic module for representing and manipulating file sizes with different prefix notations (file size unit conversion)
Project-URL: Homepage, https://bitmath.readthedocs.io/en/latest/index.html
Project-URL: Bug Tracker, https://github.com/timlnx/bitmath/issues
Project-URL: Git Repo, https://github.com/timlnx/bitmath
Author-email: Tim Case <bitmath@lnx.cx>
License-File: LICENSE
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Information Technology
Classifier: Intended Audience :: System Administrators
Classifier: Intended Audience :: Telecommunications Industry
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Communications :: File Sharing
Classifier: Topic :: Internet
Classifier: Topic :: Scientific/Engineering
Classifier: Topic :: Scientific/Engineering :: Information Analysis
Classifier: Topic :: Scientific/Engineering :: Mathematics
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Software Development :: Testing
Classifier: Topic :: Software Development :: Testing :: Acceptance
Classifier: Topic :: Software Development :: Testing :: Unit
Classifier: Topic :: System :: Filesystems
Classifier: Topic :: System :: Systems Administration
Classifier: Topic :: Text Processing :: Filters
Classifier: Topic :: Utilities
Requires-Python: >=3.9
Description-Content-Type: text/x-rst

.. image:: https://readthedocs.org/projects/bitmath/badge/?version=latest
   :target: http://bitmath.rtfd.org/
   :align: right
   :height: 19
   :width: 77

.. image:: https://github.com/timlnx/bitmath/actions/workflows/python.yml/badge.svg
   :target: https://github.com/timlnx/bitmath/actions/workflows/python.yml

.. image:: https://img.shields.io/github/issues/timlnx/bitmath?style=flat-square
   :target: https://github.com/timlnx/bitmath/issues
   :alt: Open issues

.. image:: https://img.shields.io/github/issues-pr/timlnx/bitmath?style=flat-square
   :target: https://github.com/timlnx/bitmath/pulls
   :alt: Open pull requests

.. image:: https://img.shields.io/pypi/dm/bitmath?style=flat-square
   :target: https://pypistats.org/packages/bitmath
   :alt: PyPI - Package Downloads

.. image:: https://img.shields.io/github/stars/timlnx/bitmath?style=flat-square
   :target: https://pypistats.org/packages/bitmath
   :alt: GitHub Project Popularity

.. image:: https://img.shields.io/pypi/l/bitmath?style=flat-square
   :target: https://opensource.org/licenses/MIT
   :alt: PyPI - License

.. image:: https://img.shields.io/pypi/implementation/bitmath?style=flat-square
   :alt: PyPI - Implementation

.. image:: https://img.shields.io/pypi/pyversions/bitmath?style=flat-square
   :alt: PyPI - Python Version


bitmath
=======

`bitmath <http://bitmath.readthedocs.org/en/latest/>`_ 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 <https://pypi.python.org/pypi/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 <https://docs.python.org/3/library/argparse.html>`_
  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
<https://docs.python.org/3/library/string.html>`_.

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
<https://github.com/timlnx/bitmath>`_.

And did we mention there are nearly 300 unit tests? `Check them out for
yourself <https://github.com/timlnx/bitmath/tree/master/tests>`_.

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 <https://devguide.python.org/versions/>`_ Python releases.


.. code-block:: bash

   $ sudo dnf install python3-bitmath


**PyPI**:

You could also install bitmath from `PyPI
<https://pypi.org/project/bitmath/>`_ if you like:

.. code-block:: bash

   $ pip install --user bitmath



**Source**:

To install from source, clone the repository and use pip:

.. code-block:: bash

   $ git clone https://github.com/timlnx/bitmath.git
   $ cd bitmath
   $ pip install .

To also install the ``bitmath`` manpage:

.. code-block:: bash

   $ sudo make install


Documentation
=============

The main documentation lives at
`http://bitmath.readthedocs.org/en/latest/
<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
----------

.. code-block:: python

   >>> 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).

.. code-block:: python

   >>> 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.):

.. code-block:: python

   >>> (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:

.. code-block:: python

   >>> 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:

.. code-block:: python

   >>> 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
----------------------------

.. code-block:: python

   >>> 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
---------------

.. code-block:: python

   >>> 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
-------

.. code-block:: python

   >>> 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:

.. code-block:: python

   >>> 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()**

.. code-block:: python

   >>> print(bitmath.getsize('python-bitmath.spec'))
   3.7060546875 KiB

**bitmath.parse_string()**

Parse a string with standard units:

.. code-block:: python

   >>> 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:

.. code-block:: python

   >>> 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()**

.. code-block:: python

   >>> 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()**

.. code-block:: python

   >>> 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
----------

.. code-block:: python

   >>> 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
<https://bitmath.readthedocs.io/en/latest/integration_examples.html#argparse>`_
chapter of the documentation.

.. code-block:: python

   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
