Skip to content

Commit 8802bcd

Browse files
committed
Fix best_prefix() to preserve unit family; closes #95
Bit-family instances (Bit, Kib, Mib, kb, Mb, etc.) now always return a Bit-family result from best_prefix(). Previously the conversion method name was built with a hardcoded 'B' suffix, so e.g. Bit(30950093).best_prefix() returned MiB instead of Mib. Changes: - Replace 'type(self) is Byte' exact-type check with isinstance(self, Byte), removing the pylint unidiomatic-typecheck suppression - At _index == 0 (below the first prefix threshold), Bit-family inputs now return Bit.from_other(self) rather than Byte, preserving family consistency down to the floor - Conversion method name now uses 'b' suffix for Bit-family and 'B' suffix for Byte-family inputs - Updated best_prefix() docstring with revised index-0 rule and a versionchanged:: 2.0.0 note - Updated test_bit_round_up to document the new family-preserving behaviour (Bit(16).best_prefix() -> Bit, not Byte)
1 parent 490b8e5 commit 8802bcd

2 files changed

Lines changed: 32 additions & 11 deletions

File tree

bitmath/__init__.py

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@ def from_other(cls, item):
379379
######################################################################
380380
# The following implement the Python datamodel customization methods
381381
#
382-
# Reference: http://docs.python.org/2.7/reference/datamodel.html#basic-customization
382+
# Reference: https://docs.python.org/3/reference/datamodel.html#basic-customization
383383

384384
def __repr__(self):
385385
"""Representation of this object as you would expect to see in an
@@ -430,7 +430,8 @@ def best_prefix(self, system=None):
430430
Else, begin by recording the unit system the instance is defined
431431
by. This determines which steps (NIST_STEPS/SI_STEPS) we iterate over.
432432
433-
If the instance is not already a ``Byte`` instance, convert it to one.
433+
If the instance is not already a ``Byte`` instance, convert it to one
434+
for the purpose of the log calculation.
434435
435436
NIST units step up by powers of 1024, SI units step up by powers of
436437
1000.
@@ -444,18 +445,28 @@ def best_prefix(self, system=None):
444445
This will return a value >= 0. The following determines the 'best
445446
prefix unit' for representation:
446447
447-
* result == 0, best represented as a Byte
448+
* result == 0, best represented as a Byte (or Bit for Bit-family inputs)
448449
* result >= len(SYSTEM_STEPS), best represented as an Exbi/Exabyte
449450
* 0 < result < len(SYSTEM_STEPS), best represented as SYSTEM_PREFIXES[result-1]
450451
452+
Unit family is preserved: Bit-family instances (Bit, Kib, Mib, kb,
453+
Mb, etc.) always return a Bit-family result. Byte-family instances
454+
always return a Byte-family result.
455+
456+
.. versionchanged:: 2.0.0
457+
Bit-family instances now return Bit-family results. Previously,
458+
``best_prefix()`` always returned a Byte-family unit regardless of
459+
the input type (e.g. ``Bit(30950093).best_prefix()`` returned
460+
``MiB`` instead of ``Mib``). See GitHub issue #95.
461+
451462
"""
452463

453464
# Use absolute value so we don't return Bit's for *everything*
454465
# less than Byte(1). From github issue #55
455466
if abs(self) < Byte(1):
456467
return Bit.from_other(self)
457468
else:
458-
if type(self) is Byte: # pylint: disable=unidiomatic-typecheck
469+
if isinstance(self, Byte):
459470
_inst = self
460471
else:
461472
_inst = Byte.from_other(self)
@@ -491,7 +502,10 @@ def best_prefix(self, system=None):
491502
# in the list.
492503

493504
if _index == 0:
494-
# Already a Byte() type, so return it.
505+
# Below the first prefix threshold. Bit-family inputs return as
506+
# Bit to preserve family; Byte-family inputs return as Byte.
507+
if isinstance(self, Bit):
508+
return Bit.from_other(self)
495509
return _inst
496510
elif _index >= len(_STEPS):
497511
# This is a really big number. Use the biggest prefix we've got
@@ -500,9 +514,12 @@ def best_prefix(self, system=None):
500514
# There is an appropriate prefix unit to represent this
501515
_best_prefix = _STEPS[_index - 1]
502516

503-
_conversion_method = getattr(
504-
self,
505-
'to_%sB' % _best_prefix)
517+
# Preserve unit family: Bit-family -> 'to_Xib'/'to_Xb',
518+
# Byte-family -> 'to_XiB'/'to_XB'.
519+
if isinstance(self, Bit):
520+
_conversion_method = getattr(self, 'to_%sb' % _best_prefix)
521+
else:
522+
_conversion_method = getattr(self, 'to_%sB' % _best_prefix)
506523

507524
return _conversion_method()
508525

tests/test_best_prefix_BASE.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,11 +42,15 @@ def test_byte_round_down(self):
4242
self.assertIs(type(half_byte.best_prefix()), bitmath.Bit)
4343

4444
def test_bit_round_up(self):
45-
"""best_prefix_base: 2 Bytes (as a Bit()) round up into a Byte()"""
45+
"""best_prefix_base: 2 Bytes (as a Bit()) stays as Bit() — family preserved
46+
47+
Bit(16) is 2 bytes, below the Kib/kb threshold. There is no sub-kibibit
48+
prefix unit, so best_prefix returns Bit to preserve the Bit family.
49+
Prior to 2.0.0 this returned Byte(2).
50+
"""
4651
# Two bytes is 16 bits
4752
two_bytes = bitmath.Bit(bytes=2)
48-
# Bit(16) should round up into Byte(2)
49-
self.assertIs(type(two_bytes.best_prefix()), bitmath.Byte)
53+
self.assertIs(type(two_bytes.best_prefix()), bitmath.Bit)
5054

5155
def test_byte_no_rounding(self):
5256
"""best_prefix_base: 1 Byte (as a Byte()) best prefix is still a Byte()"""

0 commit comments

Comments
 (0)