2015-01-01 18:45:39 +01:00
|
|
|
from exceptions import UnitError
|
|
|
|
|
|
|
|
|
|
|
|
def onlybytes(msg):
|
2016-06-04 11:35:59 +02:00
|
|
|
def decorator(func):
|
|
|
|
def check_other(self, other):
|
|
|
|
if not isinstance(other, Bytes):
|
|
|
|
raise UnitError(msg)
|
|
|
|
return func(self, other)
|
|
|
|
return check_other
|
|
|
|
return decorator
|
2014-01-19 12:39:07 +01:00
|
|
|
|
|
|
|
|
|
|
|
class Bytes(object):
|
|
|
|
|
2016-06-04 11:35:59 +02:00
|
|
|
units = {'B': 1,
|
|
|
|
'KiB': 1024,
|
|
|
|
'MiB': 1024 * 1024,
|
|
|
|
'GiB': 1024 * 1024 * 1024,
|
|
|
|
'TiB': 1024 * 1024 * 1024 * 1024,
|
|
|
|
}
|
|
|
|
|
|
|
|
def __init__(self, qty):
|
|
|
|
if isinstance(qty, (int, long)):
|
|
|
|
self.qty = qty
|
|
|
|
else:
|
|
|
|
self.qty = Bytes.parse(qty)
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def parse(qty_str):
|
|
|
|
import re
|
|
|
|
regex = re.compile('^(?P<qty>\d+)(?P<unit>[KMGT]i?B|B)$')
|
|
|
|
parsed = regex.match(qty_str)
|
|
|
|
if parsed is None:
|
|
|
|
raise UnitError('Unable to parse ' + qty_str)
|
|
|
|
|
|
|
|
qty = int(parsed.group('qty'))
|
|
|
|
unit = parsed.group('unit')
|
|
|
|
if unit[0] in 'KMGT':
|
|
|
|
unit = unit[0] + 'iB'
|
|
|
|
byte_qty = qty * Bytes.units[unit]
|
|
|
|
return byte_qty
|
|
|
|
|
|
|
|
def get_qty_in(self, unit):
|
|
|
|
if unit[0] in 'KMGT':
|
|
|
|
unit = unit[0] + 'iB'
|
|
|
|
if unit not in Bytes.units:
|
|
|
|
raise UnitError('Unrecognized unit: ' + unit)
|
|
|
|
if self.qty % Bytes.units[unit] != 0:
|
|
|
|
msg = 'Unable to convert {qty} bytes to a whole number in {unit}'.format(qty=self.qty, unit=unit)
|
|
|
|
raise UnitError(msg)
|
|
|
|
return self.qty / Bytes.units[unit]
|
|
|
|
|
|
|
|
def __repr__(self):
|
|
|
|
converted = str(self.get_qty_in('B')) + 'B'
|
|
|
|
if self.qty == 0:
|
|
|
|
return converted
|
|
|
|
for unit in ['TiB', 'GiB', 'MiB', 'KiB']:
|
|
|
|
try:
|
|
|
|
converted = str(self.get_qty_in(unit)) + unit
|
|
|
|
break
|
|
|
|
except UnitError:
|
|
|
|
pass
|
|
|
|
return converted
|
|
|
|
|
|
|
|
def __str__(self):
|
|
|
|
return self.__repr__()
|
|
|
|
|
|
|
|
def __int__(self):
|
|
|
|
return self.qty
|
|
|
|
|
|
|
|
def __long__(self):
|
|
|
|
return self.qty
|
|
|
|
|
|
|
|
@onlybytes('Can only compare Bytes to Bytes')
|
|
|
|
def __lt__(self, other):
|
|
|
|
return self.qty < other.qty
|
|
|
|
|
|
|
|
@onlybytes('Can only compare Bytes to Bytes')
|
|
|
|
def __le__(self, other):
|
|
|
|
return self.qty <= other.qty
|
|
|
|
|
|
|
|
@onlybytes('Can only compare Bytes to Bytes')
|
|
|
|
def __eq__(self, other):
|
|
|
|
return self.qty == other.qty
|
|
|
|
|
|
|
|
@onlybytes('Can only compare Bytes to Bytes')
|
|
|
|
def __ne__(self, other):
|
|
|
|
return self.qty != other.qty
|
|
|
|
|
|
|
|
@onlybytes('Can only compare Bytes to Bytes')
|
|
|
|
def __ge__(self, other):
|
|
|
|
return self.qty >= other.qty
|
|
|
|
|
|
|
|
@onlybytes('Can only compare Bytes to Bytes')
|
|
|
|
def __gt__(self, other):
|
|
|
|
return self.qty > other.qty
|
|
|
|
|
|
|
|
@onlybytes('Can only add Bytes to Bytes')
|
|
|
|
def __add__(self, other):
|
|
|
|
return Bytes(self.qty + other.qty)
|
|
|
|
|
|
|
|
@onlybytes('Can only add Bytes to Bytes')
|
|
|
|
def __iadd__(self, other):
|
|
|
|
self.qty += other.qty
|
|
|
|
return self
|
|
|
|
|
|
|
|
@onlybytes('Can only subtract Bytes from Bytes')
|
|
|
|
def __sub__(self, other):
|
|
|
|
return Bytes(self.qty - other.qty)
|
|
|
|
|
|
|
|
@onlybytes('Can only subtract Bytes from Bytes')
|
|
|
|
def __isub__(self, other):
|
|
|
|
self.qty -= other.qty
|
|
|
|
return self
|
|
|
|
|
|
|
|
def __mul__(self, other):
|
|
|
|
if not isinstance(other, (int, long)):
|
|
|
|
raise UnitError('Can only multiply Bytes with integers')
|
|
|
|
return Bytes(self.qty * other)
|
|
|
|
|
|
|
|
def __imul__(self, other):
|
|
|
|
if not isinstance(other, (int, long)):
|
|
|
|
raise UnitError('Can only multiply Bytes with integers')
|
|
|
|
self.qty *= other
|
|
|
|
return self
|
|
|
|
|
|
|
|
def __div__(self, other):
|
|
|
|
if isinstance(other, Bytes):
|
|
|
|
return self.qty / other.qty
|
|
|
|
if not isinstance(other, (int, long)):
|
|
|
|
raise UnitError('Can only divide Bytes with integers or Bytes')
|
|
|
|
return Bytes(self.qty / other)
|
|
|
|
|
|
|
|
def __idiv__(self, other):
|
|
|
|
if isinstance(other, Bytes):
|
|
|
|
self.qty /= other.qty
|
|
|
|
else:
|
|
|
|
if not isinstance(other, (int, long)):
|
|
|
|
raise UnitError('Can only divide Bytes with integers or Bytes')
|
|
|
|
self.qty /= other
|
|
|
|
return self
|
|
|
|
|
|
|
|
@onlybytes('Can only take modulus of Bytes with Bytes')
|
|
|
|
def __mod__(self, other):
|
|
|
|
return Bytes(self.qty % other.qty)
|
|
|
|
|
|
|
|
@onlybytes('Can only take modulus of Bytes with Bytes')
|
|
|
|
def __imod__(self, other):
|
|
|
|
self.qty %= other.qty
|
|
|
|
return self
|
|
|
|
|
|
|
|
def __getstate__(self):
|
|
|
|
return {'__class__': self.__module__ + '.' + self.__class__.__name__,
|
|
|
|
'qty': self.qty,
|
|
|
|
}
|
|
|
|
|
|
|
|
def __setstate__(self, state):
|
|
|
|
self.qty = state['qty']
|