Skip to content

Commit fe61997

Browse files
authored
Merge pull request #119 from timlnx/docsup
Refreshing docs
2 parents 8b9964c + c72f297 commit fe61997

7 files changed

Lines changed: 206 additions & 31 deletions

File tree

README.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,7 +433,7 @@ chapter of the documentation.
433433
return bitmath.parse_string(value)
434434
except ValueError:
435435
raise argparse.ArgumentTypeError(
436-
f"{value!r} is not a recognised bitmath unit string"
436+
f"{value!r} is not a recognized bitmath unit string"
437437
)
438438
439439
parser = argparse.ArgumentParser()

bitmath/__init__.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1516,8 +1516,8 @@ def parse_string(s, system=NIST, strict=True):
15161516
15171517
.. versionchanged:: 2.0.0
15181518
Added ``strict`` and ``system`` parameters. When ``strict=True``
1519-
(default) behaviour is identical to the original function.
1520-
When ``strict=False`` the behaviour of the former
1519+
(default) behavior is identical to the original function.
1520+
When ``strict=False`` the behavior of the former
15211521
``parse_string_unsafe`` is applied. The ``system`` parameter
15221522
defaults to ``bitmath.NIST`` and is ignored when ``strict=True``.
15231523
"""

docsite/source/appendices/mixed_math.rst

Lines changed: 58 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -69,37 +69,57 @@ operation.
6969
The result will be of the type of the LHS.
7070

7171
*Multiplication*
72-
Supported, but yields strange results.
72+
Supported, but yields results which may not be intuitive. The math is
73+
performed at the byte-level (ignoring prefix units). The result is in the
74+
unit of the LHS. Technically speaking, if the LHS of the equation is a byte
75+
unit, then the result should be squared. You are advised to call the
76+
:py:meth:`best_prefix` method on the result to get a useful value back.
7377

7478
.. code-block:: python
75-
:linenos:
76-
:emphasize-lines: 6,9
7779
78-
In [10]: first = MiB(5)
80+
>>> bitmath.kB(3).bytes, bitmath.MiB(5).bytes
81+
(3000.0, 5242880.0)
82+
>>> bitmath.best_prefix(3000 * 5242880)
83+
GiB(14.6484375)
7984
80-
In [11]: second = kB(2)
85+
>>> bitmath.Byte(3000) * bitmath.MiB(5)
86+
B(15728640000.0)
8187
82-
In [12]: first * second
83-
Out[12]: MiB(10000.0)
88+
>>> (bitmath.Byte(3000) * bitmath.MiB(5)).best_prefix()
89+
GiB(14.6484375)
8490
85-
In [13]: (first * second).best_prefix()
86-
Out[13]: GiB(9.765625)
91+
The final result represents how many “GiB-sized” chunks of bytes are in that
92+
total. It's weird, but it works.
8793

88-
As we can see on lines **6** and **9**, multiplying even two
89-
relatively small quantities together (``MiB(5)`` and ``kB(2)``) yields
90-
quite large results.
94+
If the LHS is larger than a byte prefix unit then you get a result matching the
95+
unit of the LHS of the equation.
9196

92-
Internally, this is implemented as:
93-
94-
.. math::
97+
.. code-block:: python
9598
96-
(5 \cdot 2^{20}) \cdot (2 \cdot 10^{3}) = 10,485,760,000 B
99+
>>> bitmath.MiB(5) * bitmath.kB(3)
100+
MiB(15000.0)
101+
# LHS was MiB, result is MiB
97102
98-
10,485,760,000 B \cdot \dfrac{1 MiB}{1,048,576 B} = 10,000 MiB
103+
# And if we wrap it with best_prefix() we get the earlier result back
104+
>>> (bitmath.MiB(5) * bitmath.kB(3)).best_prefix()
105+
GiB(14.6484375)
99106
100107
*Division*
101108
The result will be a number type due to unit cancellation.
102109

110+
.. code-block:: python
111+
112+
>>> bitmath.kB(3) / bitmath.MiB(5)
113+
0.00057220458984375
114+
115+
>>> bitmath.kB(3).bytes / bitmath.MiB(5).bytes
116+
0.00057220458984375
117+
118+
Above you can see that the math is performed on the bytes of each operand. As
119+
noted above, the units cancel out. This is the opposite of the multiplication
120+
case where the units square together if you do not coerce them into a larger
121+
prefix unit.
122+
103123
.. _appendix_math_mixed_types:
104124

105125
Mixed Types: Addition and Subtraction
@@ -123,7 +143,7 @@ function works correctly with iterables of bitmath objects, since
123143
>>> sum([bitmath.Byte(1), bitmath.MiB(1), bitmath.GiB(1)])
124144
Byte(1074790401.0)
125145
126-
For all non-zero numeric operands the behaviour (returning a number)
146+
For all non-zero numeric operands the behavior (returning a number)
127147
applies.
128148

129149
**Discussion:** Why do ``100 - KiB(90)`` and ``KiB(100) - 90`` both
@@ -251,7 +271,25 @@ yourself what you would expect to get if you did this:
251271

252272
.. math::
253273
254-
\dfrac{100}{kB(33)} = x
274+
\dfrac{100}{kB(33)}
275+
276+
Unless you're representing rates that doesn't mean much at all. The units of
277+
operands when expressing rates is going to be context sensitive and can be very
278+
non-intuitive without additional knowledge. For example:
279+
280+
.. code-block:: python
281+
282+
>>> 100/bitmath.kB(33)
283+
3.0303030303030303
284+
285+
This is functionally equivalent to writing:
286+
287+
.. math::
288+
289+
\dfrac{1}{kB(33)} \cdot 100
290+
291+
This might mean something to you, but we can't express that as a prefix unit. We
292+
let you do it, but it is up to you to determine the significance of the result.
255293

256294

257295

@@ -326,6 +364,6 @@ Footnotes
326364
<https://www.programiz.com/python-programming/precedence-associativity>`_
327365
328366
.. [#datamodel] `Python Datamodel Customization Methods
329-
<https://docs.python.org/2.7/reference/datamodel.html#basic-customization>`_
367+
<https://docs.python.org/3/reference/datamodel.html#basic-customization>`_
330368
331369
.. [#significance] https://en.wikipedia.org/wiki/Significance_arithmetic

docsite/source/index.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -378,7 +378,7 @@ chapter of the documentation.
378378
return bitmath.parse_string(value)
379379
except ValueError:
380380
raise argparse.ArgumentTypeError(
381-
f"{value!r} is not a recognised bitmath unit string"
381+
f"{value!r} is not a recognized bitmath unit string"
382382
)
383383
384384
parser = argparse.ArgumentParser()

docsite/source/integration_examples.rst

Lines changed: 77 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ directly.
4040
return bitmath.parse_string(value)
4141
except ValueError:
4242
raise argparse.ArgumentTypeError(
43-
f"{value!r} is not a recognised bitmath unit string "
43+
f"{value!r} is not a recognized bitmath unit string "
4444
"(examples: 10MiB, 1.5GiB, 500kB)"
4545
)
4646
@@ -72,7 +72,80 @@ Example run:
7272
In KiB: 10240.00 KiB
7373
7474
$ python script.py --block-size bad
75-
error: argument --block-size: 'bad' is not a recognised bitmath unit string (examples: 10MiB, 1.5GiB, 500kB)
75+
error: argument --block-size: 'bad' is not a recognized bitmath unit string (examples: 10MiB, 1.5GiB, 500kB)
76+
77+
argparse validation
78+
===================
79+
80+
Now say you want to perform some additional validation on this custom
81+
``BitmathType`` unit. This is best done **after** the parsing is complete, not
82+
as part of the ``BitmathType`` implementation. This follows the advice outlined
83+
in the upstream :mod:`argparse` documentation.
84+
85+
In general, the ``type`` keyword is a convenience that should only be used for
86+
simple conversions that can only raise one of the three supported exceptions.
87+
Anything with more interesting error-handling or resource management should be
88+
done downstream after the arguments are parsed.
89+
90+
First, parse the unit, allow ``BitmathType`` to handle the parsing validation.
91+
Second, perform your own context-aware validation. For example, you might set
92+
minimum or maximums and need to compare the parsed argument against them.
93+
94+
.. code-block:: python
95+
:linenos:
96+
:emphasize-lines: 17,29-30
97+
98+
import argparse
99+
import bitmath
100+
101+
102+
def BitmathType(value):
103+
"""Convert a command-line string such as '10MiB' into a bitmath object."""
104+
try:
105+
return bitmath.parse_string(value)
106+
except ValueError:
107+
raise argparse.ArgumentTypeError(
108+
f"{value!r} is not a recognized bitmath unit string "
109+
"(examples: 10MiB, 1.5GiB, 500kB)"
110+
)
111+
112+
113+
def main():
114+
max_block_size = bitmath.GiB(1)
115+
parser = argparse.ArgumentParser(
116+
description="Example script using a bitmath argument type"
117+
)
118+
parser.add_argument(
119+
"--block-size",
120+
type=BitmathType,
121+
required=True,
122+
help="Block size with unit, e.g. 10MiB",
123+
)
124+
args = parser.parse_args()
125+
126+
if args.block_size > bitmath.GiB(1):
127+
raise ValueError(f"Provided block size {args.block_size} exceeds maximum {max_block_size}")
128+
129+
print(f"Block size: {args.block_size}")
130+
print(f"In KiB: {args.block_size.to_KiB():.2f}")
131+
132+
133+
if __name__ == "__main__":
134+
main()
135+
136+
Example run:
137+
138+
.. code-block:: bash
139+
140+
$ python script.py --block-size 42GiB
141+
Traceback (most recent call last):
142+
File "script.py", line 37, in <module>
143+
main()
144+
~~~~^^
145+
File "script.py", line 30, in main
146+
raise ValueError(f"Provided block size {args.block_size} exceeds maximum {max_block_size}")
147+
ValueError: Provided block size 42.0 GiB exceeds maximum 1.0 GiB
148+
76149
77150
78151
.. _integration_examples_click:
@@ -108,7 +181,7 @@ Install click before use:
108181
return bitmath.parse_string(value)
109182
except ValueError:
110183
self.fail(
111-
f"{value!r} is not a recognised bitmath unit string "
184+
f"{value!r} is not a recognized bitmath unit string "
112185
"(examples: 10MiB, 1.5GiB, 500kB)",
113186
param,
114187
ctx,
@@ -143,7 +216,7 @@ Example run:
143216
In KiB: 10240.00 KiB
144217
145218
$ python script.py --block-size bad
146-
Error: Invalid value for '--block-size': 'bad' is not a recognised bitmath unit string (examples: 10MiB, 1.5GiB, 500kB)
219+
Error: Invalid value for '--block-size': 'bad' is not a recognized bitmath unit string (examples: 10MiB, 1.5GiB, 500kB)
147220
148221
149222
.. _integration_examples_progressbar2:

docsite/source/module.rst

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,24 @@ bitmath.listdir()
153153
The **total** size of the files in this tree is **1337 + 13370 =
154154
14707** bytes.
155155

156+
.. versionadded:: 2.0.0
157+
158+
By far the simplest way to sum all of the results is using the built-in
159+
:py:func:`sum` function, or :py:func:`bitmath.sum` for additional control
160+
(complete docs on that following this section).
161+
162+
.. code-block:: python
163+
164+
>>> discovered_files = [f[1] for f in bitmath.listdir('./some_files')]
165+
>>> print(discovered_files)
166+
[Byte(1337.0), Byte(13370.0)]
167+
>>> print(sum(discovered_files))
168+
14707.0 B
169+
>>> print(sum(discovered_files).best_prefix())
170+
14.3623046875 KiB
171+
172+
173+
156174
Let's call :py:func:`bitmath.listdir` on the ``some_files/``
157175
directory and see what the results look like. First we'll use all
158176
the default parameters, then we'll set ``relpath`` to ``True``:
@@ -391,7 +409,7 @@ bitmath.parse_string()
391409

392410
.. versionchanged:: 2.0.0
393411
Added ``strict`` and ``system`` parameters. The default
394-
``strict=True`` behaviour is identical to earlier versions.
412+
``strict=True`` behavior is identical to earlier versions.
395413
``system`` defaults to :py:data:`bitmath.NIST` and is only
396414
consulted when ``strict=False``.
397415

@@ -406,7 +424,7 @@ parse_string with ``strict=False``
406424
When ``strict=False`` the parser accepts ambiguous input that does not
407425
conform to exact bitmath type names — for example, the single-letter
408426
units produced by tools like ``ls -h``, ``df``, and ``qemu-img``. This
409-
is the behaviour previously provided by the now-deprecated
427+
is the behavior previously provided by the now-deprecated
410428
:py:func:`bitmath.parse_string_unsafe`.
411429

412430
All inputs are treated as **byte-based**. Bit-based units are not
@@ -524,7 +542,7 @@ bitmath.parse_string_unsafe()
524542
.. function:: parse_string_unsafe(repr[, system=bitmath.NIST])
525543

526544
A deprecated thin wrapper around
527-
``parse_string(repr, strict=False, system=system)``. All behaviour,
545+
``parse_string(repr, strict=False, system=system)``. All behavior,
528546
parameters, and caveats are identical to
529547
:ref:`parse_string with strict=False <parse-string-non-strict>`.
530548

docsite/source/simple_examples.rst

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,52 @@ Unit Conversion
141141
>>> fourty_two_mib.KiB
142142
KiB(43008.0)
143143
144+
Let's convert a unit and show the differences. What happens if we convert ``2048
145+
MB`` into GiBs? Let's look the MB unit in closer detail:
146+
147+
.. code-block:: python
148+
149+
>>> from bitmath import *
150+
>>> MB(2048).base, MB(2048).power, MB(2048).bytes
151+
(10, 6, 2048000000.0)
152+
153+
An MB (megabyte) is an SI unit in the base-10 number system. A single megabyte
154+
is 10 raised to the power of 6. When you raise 10 to the power of 6 and multiply
155+
the result by 2048 you get the number of bytes in ``MB(2048)``. We can check
156+
this by creating a byte object with that many bytes as the value and ask for the
157+
``MB`` equivalent:
158+
159+
.. code-block:: python
160+
161+
>>> bitmath.Byte((10**6)*2048).MB
162+
MB(2048.0)
163+
164+
What about that conversion though, how about we convert this into GiBs? Those
165+
are NIST units, it is a base-2 number system. Let's convert the MB to a GiB and
166+
look at those instance attributes again:
167+
168+
.. code-block:: python
169+
170+
>>> convert_demo = bitmath.MB(2048).GiB
171+
>>> convert_demo
172+
GiB(1.9073486328125)
173+
>>> convert_demo.base, convert_demo.power, convert_demo.bytes
174+
(2, 30, 2048000000.0)
175+
176+
We can see that the entire calculation has changed but the number of bytes has
177+
remained the same. Now we have 2 raised to the power of 6, times
178+
1.9073486328125.
179+
180+
A cleaner looking conversion is possible if we convert this within the same unit
181+
system. If we look at the GB equivalent we get:
182+
183+
.. code-block:: python
184+
185+
>>> convert_demo.GB
186+
GB(2.048)
187+
188+
Because we aren't shifting between different base number systems.
189+
144190
Rich Comparison
145191
***************
146192

0 commit comments

Comments
 (0)