def bankersround(value, ndigits=0): """Round a number to a given precision. Works like round() except that the round-half-even (banker's rounding) algorithm is used instead of round-half-up. >>> bankersround(5.5, 0) 6.0 >>> bankersround(6.5, 0) 6.0 >>> bankersround(-6.5, 0) -6.0 >>> bankersround(1234.0, -2) 1200.0 """ sign = int(value < 0) and -1 or 1 value = abs(value) a, b = split_number(value) digits = a + b add = 0 i = len(a) + ndigits if i < 0 or i >= len(digits): pass elif digits[i] > '5': add = 1 elif digits[i] == '5' and digits[i-1] in '13579': add = 1 elif digits[i] == '5': # previous digit is even # We round up unless all following digits are zero. for j in range_type(i + 1, len(digits)): if digits[j] != '0': add = 1 break scale = 10**ndigits if isinstance(value, Decimal): return Decimal(int(value * scale + add)) / scale * sign else: return float(int(value * scale + add)) / scale * sign
def read_mo(fileobj): """Read a binary MO file from the given file-like object and return a corresponding `Catalog` object. :param fileobj: the file-like object to read the MO file from :note: The implementation of this function is heavily based on the ``GNUTranslations._parse`` method of the ``gettext`` module in the standard library. """ catalog = Catalog() headers = {} filename = getattr(fileobj, 'name', '') buf = fileobj.read() buflen = len(buf) unpack = struct.unpack # Parse the .mo file header, which consists of 5 little endian 32 # bit words. magic = unpack('<I', buf[:4])[0] # Are we big endian or little endian? if magic == LE_MAGIC: version, msgcount, origidx, transidx = unpack('<4I', buf[4:20]) ii = '<II' elif magic == BE_MAGIC: version, msgcount, origidx, transidx = unpack('>4I', buf[4:20]) ii = '>II' else: raise IOError(0, 'Bad magic number', filename) # Now put all messages from the .mo file buffer into the catalog # dictionary for i in range_type(0, msgcount): mlen, moff = unpack(ii, buf[origidx:origidx + 8]) mend = moff + mlen tlen, toff = unpack(ii, buf[transidx:transidx + 8]) tend = toff + tlen if mend < buflen and tend < buflen: msg = buf[moff:mend] tmsg = buf[toff:tend] else: raise IOError(0, 'File is corrupt', filename) # See if we're looking at GNU .mo conventions for metadata if mlen == 0: # Catalog description lastkey = key = None for item in tmsg.splitlines(): item = item.strip() if not item: continue if b':' in item: key, value = item.split(b':', 1) lastkey = key = key.strip().lower() headers[key] = value.strip() elif lastkey: headers[lastkey] += b'\n' + item if b'\x04' in msg: # context ctxt, msg = msg.split(b'\x04') else: ctxt = None if b'\x00' in msg: # plural forms msg = msg.split(b'\x00') tmsg = tmsg.split(b'\x00') if catalog.charset: msg = [x.decode(catalog.charset) for x in msg] tmsg = [x.decode(catalog.charset) for x in tmsg] else: if catalog.charset: msg = msg.decode(catalog.charset) tmsg = tmsg.decode(catalog.charset) catalog[msg] = Message(msg, tmsg, context=ctxt) # advance to next entry in the seek tables origidx += 8 transidx += 8 catalog.mime_headers = headers.items() return catalog