Mike Orr avatar Mike Orr committed 7b23469

``format_data_size()`` helpers for SI units (1024 = "1.2 kB" = "1.0 KiB").

Comments (0)

Files changed (5)

 
   - New function ``deprecate``.
 
+* webhelpers.number:
+
+  - New functions ``format_data_size``, ``format_byte_size``, and
+    ``format_bit_size`` for displaying numbers in SI units
+    ("1.2 kilobytes", "1.2 kB", "1.0 KiB").
+
 1.0b5 (2010-03-18)
 ------------------
 

docs/modules/number.rst

 ---------
 
 .. autofunction:: percent_of
+.. autofunction:: format_data_size
+.. autofunction:: format_byte_size
+.. autofunction:: format_bit_size
+
 .. autofunction:: mean
 .. function:: average(r)
 

docs/whats_new.rst

 ``OverwriteError``, a ``DeclarativeException`` class for making your own
 exceptions with constant messages, and a ``deprecate`` functioand n.
 
+webhelpers.number
++++++++++++++++++
+``format_data_size()`` and its derivatives ``format_byte_size()`` and
+``format_bit_size()`` provide a convenient way to display numbers using SI
+units ("1.2 kilobytes", "1.2 kB", "1.0 KiB").
+
 webhelpers.paginate
 +++++++++++++++++++
 

tests/test_number.py

         eq_(format_number(1234.5, " ", ","), "1 234,5")
         eq_(format_number(1234.5, ".", ","), "1.234,5")
         eq_(format_number(-1234.5, ".", ","), "-1.234,5")
+
+
+class TestFormatDataSize(object):
+    def test_bytes(self):
+        eq_(  format_byte_size(1),  '1 B')
+
+    def test_kibibytes(self):
+        eq_(  format_byte_size(1000, binary=True),  '1000 B')
+        eq_(  format_byte_size(1024, 0, True),  '1 KiB')
+        eq_(  format_byte_size(1024, 2, True),  '1.00 KiB')
+
+    def test_kilobytes(self):
+        eq_(  format_byte_size(1000),  '1.0 kB')
+        eq_(  format_byte_size(1024, 0, False),  '1 kB')
+        eq_(  format_byte_size(1024, 2, False),  '1.02 kB')
+        eq_(  format_byte_size(1024, 0, False, True),  '1 kilobytes')
+        eq_(  format_byte_size(1024, 2, False, True),  '1.02 kilobytes')
+
+    def test_kilobits(self):
+        eq_(  format_bit_size(1024, 0, False, False),  '1 kb')
+        eq_(  format_bit_size(1024, 2, False, False),  '1.02 kb')
+        eq_(  format_bit_size(1024, 0, False, True),  '1 kilobits')
+        eq_(  format_bit_size(1024, 2, False, True),  '1.02 kilobits')
+
+    def test_megabytes(self):
+        eq_(  format_byte_size(12345678, 2, True),  '11.77 MiB')
+        eq_(  format_byte_size(12345678, 2, False),  '12.35 MB')
+
+    def test_terabytes(self):
+        eq_(  format_byte_size(12345678901234, 2, True),  '11.23 TiB')
+        eq_(  format_byte_size(12345678901234, 2, False),  '12.35 TB')
+
+    def test_zettabytes(self):
+        eq_(  format_byte_size(1234567890123456789012, 2, True),  '1.05 ZiB')
+        eq_(  format_byte_size(1234567890123456789012, 2, False),  '1.23 ZB')
+
+    def test_yottabytes(self):
+        eq_(  format_byte_size(123456789012345678901234567890, 2, True),  
+            '102121.06 YiB')
+        eq_(  format_byte_size(123456789012345678901234567890, 2, False),  
+            '123456.79 YB')

webhelpers/number.py

 """Number formatting, numeric helpers, and numeric statistics"""
 
+import math
 import re
 
 def percent_of(part, whole):
     # Use float to force true division.
     return float(part * 100) / whole
 
+def format_data_size(size, unit, precision=1, binary=False, full_name=False):
+    """Format a number using SI units (kilo, mega, etc.)
+
+    ``size``: The number as a float or int.
+
+    ``unit``: The unit name in plural form. Examples: "bytes", "B".
+
+    ``precision``: How many digits to the right of the decimal point. Default
+    is 1.  0 suppresses the decimal point.
+
+    ``binary``: If false, use base-10 decimal prefixes (kilo = K = 1000).  
+    If true, use base-2 binary prefixes (kibi = Ki = 1024).  
+
+    ``full_name``: If false (default), use the prefix abbreviation ("k" or
+    "Ki").  If true, use the full prefix ("kilo" or "kibi"). If false,
+    use abbreviation ("k" or "Ki").
+
+    Examples:
+
+    >>> format_data_size(1024, "B")
+    '1.0 kB'
+
+    >>> format_data_size(1024, "B", 2)
+    '1.02 kB'
+
+    >>> format_data_size(1024, "B", 2, binary=True)
+    '1.00 KiB'
+
+    >>> format_data_size(54000, "Wh", 0)
+    '54 kWh'
+
+    >>> format_data_size(85000, "m/h", 0)
+    '85 km/h'
+
+    >>> format_data_size(85000, "m/h", 0).replace("km/h", "clicks")
+    '85 clicks'
+    """
+    if full_name is None:
+        full_name = len(unit) > 1
+        
+    if not binary:
+        base = 1000
+        if full_name:
+            multiples = ('', 'kilo', 'mega', 'giga', 'tera', 'peta', 'exa', 'zetta', 'yotta')
+        else:
+            multiples = ('', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y')
+    else:
+        base = 1024
+        if full_name:
+            multiples = ('', 'kibi', 'mebi', 'gibi', 'tebi', 'pebi', 'exbi', 'zebi', 'yobi')
+        else:
+            multiples = ('', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi')
+            
+    m = int(math.log(size) / math.log(base))
+    if m > 8:
+        m = 8
+
+    if m == 0:
+        precision = '%.0f'
+    else:
+        precision = '%%.%df' % precision
+        
+    size = precision % (size / math.pow(base, m))
+
+    return '%s %s%s' % (size.strip(), multiples[m], unit)
+
+def format_byte_size(size, precision=1, binary=False, full_name=False):
+    """Same as ``format_data_size`` but specifically for bytes.
+    
+    Examples:
+
+    >>> format_byte_size(2048)
+    '2.0 kB'
+    
+    >>> format_byte_size(2048, full_name=True)
+    '2.0 kilobytes'
+    """
+    if full_name:
+        return format_data_size(size, "bytes", precision, binary, True)
+    else:
+        return format_data_size(size, "B", precision, binary, False)
+
+def format_bit_size(size, precision=1, binary=False, full_name=False):
+    """Same as ``format_data_size`` but specifically for bytes.
+
+    Examples:
+
+    >>> format_bit_size(2048)
+    '2.0 kb'
+    
+    >>> format_bit_size(2048, full_name=True)
+    '2.0 kilobits'
+    """
+    if full_name:
+        return format_data_size(size, "bits", precision, binary, True)
+    else:
+        return format_data_size(size, "b", precision, binary, False)
+
 def mean(r):
     """Return the mean (i.e., average) of a sequence of numbers.
 
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.