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