SF.net SVN: fclient:[837] trunk/fclient/src/fclient/impl/lib/numbers.py
Status: Pre-Alpha
Brought to you by:
jurner
|
From: <jU...@us...> - 2008-07-31 16:58:34
|
Revision: 837
http://fclient.svn.sourceforge.net/fclient/?rev=837&view=rev
Author: jUrner
Date: 2008-07-31 16:58:43 +0000 (Thu, 31 Jul 2008)
Log Message:
-----------
ups, missing module
Added Paths:
-----------
trunk/fclient/src/fclient/impl/lib/numbers.py
Added: trunk/fclient/src/fclient/impl/lib/numbers.py
===================================================================
--- trunk/fclient/src/fclient/impl/lib/numbers.py (rev 0)
+++ trunk/fclient/src/fclient/impl/lib/numbers.py 2008-07-31 16:58:43 UTC (rev 837)
@@ -0,0 +1,314 @@
+"""Number crunching and others
+
+"""
+
+import re
+#***************************************************************
+#
+#***************************************************************
+class ByteSizeNames:
+ Binary = ('B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB')
+ Common = ('', 'Kb', 'Mb', 'Gb', 'Tb', 'Pb', 'Eb', 'Zb', 'Yb')
+
+
+
+def format_num_bytes(num, binary=False, names=None, format_strings=('%i%s', '%01.2f%s') ):
+ """Formats a number representing a number of bytes to a human readable string
+ @param num: (int) number to fomat
+ @param binary: if True conversion factor is 1024, else factor is 1000
+ @param names: (tuple) names to use as suffix or None to use default names (see L{ByteSizeNames})
+ @param format_strings: (tuple) format strings to be used or None to use the default formating.
+ The first member of the tuple is used to format bytes, the seconds anything > one kilobyte
+ @return: (str) formated number
+
+ >>> format_num_bytes(100)
+ '100B'
+
+ >>> format_num_bytes(1000)
+ '1.00KB'
+
+ >>> format_num_bytes(1024, binary=True)
+ '1.00KiB'
+
+ >>> format_num_bytes(1000, names=('W', 'X', 'Y', 'Z'))
+ '1.00X'
+
+ >>> format_num_bytes(1000, format_strings=('%s%i', '%01.4f%s'))
+ '1.0000KB'
+
+ """
+ if binary:
+ factor = 1024
+ if names is None:
+ names = ByteSizeNames.Binary
+ else:
+ factor = 1000
+ if names is None:
+ names = ByteSizeNames.Common
+
+ name = names[0]
+ if num >= factor:
+ num = float(num)
+ for tmp_name in names[1: ]:
+ num /= factor
+ name = tmp_name
+ if num < factor:
+ break
+ else:
+ return format_strings[0] % (num, name)
+ return format_strings[1] % (num, name)
+
+
+NumBytesPat = re.compile('''\A (\d+ \. \d+) | (\d+)''', re.X)
+def num_bytes_to_bytes(num, binary=False, names=None):
+ """Converts a string containing a bytes size to an integer
+ @param num: (str) string to convert
+ @param binary: if True conversion factor is 1024, else factor is 1000
+ @param names: (tuple) names to use as suffix or None to use default names (see L{ByteSizeNames})
+
+ @return: (int) number of bytes
+ @note: postfixes are handled case sensitive
+
+ >>> num_bytes_to_bytes('1000B')
+ 1000
+
+ >>> num_bytes_to_bytes('1GB')
+ 1000000000
+
+ >>> num_bytes_to_bytes('1.2KB')
+ 1200
+
+ >>> num_bytes_to_bytes('1.5678B')
+ 2
+
+ >>> num_bytes_to_bytes('1MiB', binary=True)
+ 1048576
+
+ >>> num_bytes_to_bytes('1X', names=('X', ))
+ 1
+
+ >>> num_bytes_to_bytes('foo')
+ Traceback (most recent call last):
+ ...
+ ValueError: No number found in input string
+
+ >>> num_bytes_to_bytes('1foo')
+ Traceback (most recent call last):
+ ...
+ ValueError: Unknown size postfix
+ """
+ if names is None:
+ if binary:
+ names = ByteSizeNames.Binary
+ else:
+ names = ByteSizeNames.Common
+ names = list(names)
+
+ match = NumBytesPat.match(num)
+ if match is None:
+ raise ValueError('No number found in input string')
+
+ isfloat, isint = match.groups()
+ if isfloat:
+ z = len(isfloat)
+ tmp_num = float(isfloat)
+ else:
+ z = len(isint)
+ tmp_num = int(isint)
+
+ postfix = num[z: ]
+ try:
+ exp = names.index(postfix)
+ except ValueError:
+ raise ValueError('Unknown size postfix')
+ factor = 1024 if binary else 1000
+
+ #TODO: 1.96B is returned rounded as 2. Should we complain?
+ return int(round(tmp_num * (factor ** exp)))
+
+
+#***************************************************************
+#
+#***************************************************************
+TimeDurationNames = {
+ 'seconds': 's',
+ 'minutes': 'm',
+ 'hours': 'h',
+ 'days': 'd',
+ 'years': 'y'
+ }
+def format_time_delta(t1, t2, names=None):
+ """Pretty prints a time delta
+ @arg t1: duration starting time
+ @arg t2: duration ending time
+ @arg names: (optional) dict mapping names to names as they should be
+ to be printed (see TimeDurationNames)
+
+ >>> import time
+ >>> t0 = time.time()
+
+ >>> format_time_delta(t0, t0 +1)
+ '1s'
+
+ >>> format_time_delta(t0, t0)
+ '0s'
+
+ >>> format_time_delta(t0, t0 +1.4)
+ '1.4s'
+
+ >>> format_time_delta(t0, t0 +60)
+ '1m'
+
+ >>> format_time_delta(t0, t0 +12345)
+ '3.4h'
+
+ >>> format_time_delta(t0, t0 +1234567890)
+ '39.1y'
+
+ >>> format_time_delta(t0, t0 +1234567890, names={'years': 'leapers', 'seconds': 's', 'minutes': 'm', 'hours': 'h', 'days': 'd'})
+ '39.1leapers'
+
+ """
+ mapping = (
+ ('years', 1),
+ ('days', 365),
+ ('hours', 24),
+ ('minutes', 60),
+ ('seconds', 60),
+ )
+ if names is None:
+ names = TimeDurationNames
+
+ delta = t2 - t1
+ if delta < 0:
+ t = n = 0
+ name = 'seconds'
+ else:
+ start = (60 * 60 * 24 * 365)
+ for name, fac in mapping:
+ start = start / fac
+ t = delta / start
+ t = round(t, 1)
+ n = int(t)
+ if n:
+ break
+
+ name = names[name]
+ if t > n:
+ return '%s%s' % (t, name)
+ else:
+ return '%s%s' % (n, name)
+
+
+#*****************************************************************
+#
+#****************************************************************
+HEX_PREFIXES = ('0x', '0X', '&H', '&h')
+NUM_DIGITS = "0123456789abcdefghijklmnopqrstuvwxyz"
+
+
+def to_base(to_base, num, base=10, hex_prefixes=HEX_PREFIXES):
+ """Converts a number to the desired base
+ @param to_base: base to convert the number to (2-36 and 256)
+ @param num: number to convert
+ @param base: base of the number to convert
+ @param hex_prefixes: prefixes for hexadecimal numbers to be recognized by the function
+
+ @return: (str) converted number
+
+
+ >>> to_base(2, 10)
+ '1010'
+ >>> to_base(10, '1010', base=2)
+ '10'
+ >>> to_base(2, '1010', base=2)
+ '1010'
+
+ >>> to_base(10, '0xFF', base=16)
+ '255'
+
+ >>> to_base(256, '0x61', base=16)
+ 'a'
+ >>> to_base(2, 'a', base=256)
+ '1100001'
+
+
+ >>> result = to_base(2, 'Hello World!', 256)
+ >>> result
+ '10010000110010101101100011011000110111100100000010101110110111101110010011011000110010000100001'
+ >>> result = to_base(256, result, base=2)
+ >>> result
+ 'Hello World!'
+
+ """
+ if num < 0:
+ raise ValueError('negative numbers not supported: %r' % num)
+ if to_base < 2 or to_base > 36 and not to_base == 256:
+ raise ValueError('bes must be in range 2-36 or 256, found: %r' % to_base)
+ if base < 2 or base > 36 and not base == 256:
+ raise ValueError('bes must be in range 2-36 or 256, found: %r' % base)
+
+ if base == 256:
+ tmp_num = 0
+ for char in num:
+ tmp_num = (tmp_num << 8) + ord(char)
+ num = tmp_num
+ else:
+ # let int() handle it
+ if base == 16:
+ num = strip_hex_prefix(num, hex_prefixes=hex_prefixes)
+ num = int('%s' % num, base)
+
+ if to_base == 10 and base != 256:
+ return str(num)
+
+ out = ''
+ if to_base == 256:
+ while num:
+ num, tail = divmod(num, to_base)
+ out += chr(tail)
+ return out[-1::-1] if out else '\x00'
+ else:
+ while num:
+ num, tail = divmod(num, to_base)
+ out += NUM_DIGITS[tail]
+ return out[-1::-1] if out else '0'
+
+
+
+def strip_hex_prefix(num, hex_prefixes=HEX_PREFIXES):
+ """Strips prefixes from hexadecimal numbers
+ @param num: number to strip prefix from
+ @param hex_prefixes: list containing prefixes to strip
+ @return: (str) stripped number
+
+ >>> strip_hex_prefix('0xFF')
+ 'FF'
+ >>> strip_hex_prefix('&HFF')
+ 'FF'
+ >>> strip_hex_prefix(10)
+ '10'
+ >>> strip_hex_prefix('')
+ ''
+
+ """
+ if not isinstance(num, basestring):
+ num = '%s' % num
+ else:
+ pat = re.compile(
+ '\A(%s)' % '|'.join([re.escape(i) for i in hex_prefixes])
+ )
+ num = pat.sub('', num)
+ return num
+
+
+#*****************************************************************
+#
+#****************************************************************
+if __name__ == '__main__':
+ import doctest
+ doctest.testmod()
+
+
+
+
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|