Merge pull request #210 from CanonicalLtd/mwhudson/dehumanize_size-sanity
support lower case suffixes in dehumanize_size
This commit is contained in:
commit
eea4240bac
|
@ -23,7 +23,7 @@ from subiquitycore.ui.error import ErrorView
|
|||
from subiquity.curtin import (curtin_write_storage_actions,
|
||||
curtin_write_preserved_actions)
|
||||
from subiquity.models import (FilesystemModel, RaidModel)
|
||||
from subiquity.models.filesystem import _humanize_size
|
||||
from subiquity.models.filesystem import humanize_size
|
||||
from subiquity.ui.views import (DiskPartitionView, AddPartitionView,
|
||||
AddFormatView, FilesystemView,
|
||||
DiskInfoView, RaidView, BcacheView,
|
||||
|
@ -301,7 +301,7 @@ class FilesystemController(BaseController):
|
|||
'model': disk.model,
|
||||
'serial': disk.serial,
|
||||
'size': disk.size,
|
||||
'humansize': _humanize_size(disk.size),
|
||||
'humansize': humanize_size(disk.size),
|
||||
'vendor': disk._info.vendor,
|
||||
'rotational': 'true' if rotational == '1' else 'false',
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ class FS:
|
|||
is_mounted = attr.ib()
|
||||
|
||||
|
||||
def _humanize_size(size):
|
||||
def humanize_size(size):
|
||||
size = abs(size)
|
||||
if size == 0:
|
||||
return "0B"
|
||||
|
@ -41,33 +41,44 @@ def _humanize_size(size):
|
|||
return "%.3f%s" % (size / math.pow(1024, p), HUMAN_UNITS[int(p)])
|
||||
|
||||
|
||||
def _dehumanize_size(size):
|
||||
def dehumanize_size(size):
|
||||
# convert human 'size' to integer
|
||||
size_in = size
|
||||
if size.endswith("B"):
|
||||
|
||||
if not size:
|
||||
raise ValueError("input cannot be empty")
|
||||
|
||||
if not size[-1].isdigit():
|
||||
suffix = size[-1].upper()
|
||||
size = size[:-1]
|
||||
else:
|
||||
suffix = None
|
||||
|
||||
# build mpliers based on HUMAN_UNITS
|
||||
mpliers = {}
|
||||
for (unit, exponent) in zip(HUMAN_UNITS, range(0, len(HUMAN_UNITS))):
|
||||
mpliers[unit] = 2 ** (exponent * 10)
|
||||
|
||||
num = size
|
||||
mplier = 'B'
|
||||
for m in mpliers:
|
||||
if size.endswith(m):
|
||||
mplier = m
|
||||
num = size[0:-len(m)]
|
||||
parts = size.split('.')
|
||||
if len(parts) > 2:
|
||||
raise ValueError("{!r} is not valid input".format(size_in))
|
||||
elif len(parts) == 2:
|
||||
div = 10**len(parts[1])
|
||||
size = parts[0] + parts[1]
|
||||
else:
|
||||
div = 1
|
||||
|
||||
try:
|
||||
num = float(num)
|
||||
num = int(size)
|
||||
except ValueError:
|
||||
raise ValueError("'{}' is not valid input.".format(size_in))
|
||||
raise ValueError("{!r} is not valid input".format(size_in))
|
||||
|
||||
if suffix is not None:
|
||||
if suffix not in HUMAN_UNITS:
|
||||
raise ValueError("unrecognized suffix {!r} in {!r}".format(size_in[-1], size_in))
|
||||
mult = 2**(10*HUMAN_UNITS.index(suffix))
|
||||
else:
|
||||
mult = 1
|
||||
|
||||
if num < 0:
|
||||
raise ValueError("'{}': cannot be negative".format(size_in))
|
||||
raise ValueError("{!r}: cannot be negative".format(size_in))
|
||||
|
||||
return int(num * mpliers[mplier])
|
||||
return num * mult // div
|
||||
|
||||
|
||||
def id_factory(name):
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
|
||||
import unittest
|
||||
|
||||
from subiquity.models.filesystem import dehumanize_size
|
||||
|
||||
class TestDehumanizeSize(unittest.TestCase):
|
||||
|
||||
basics = [
|
||||
('1', 1),
|
||||
('134', 134),
|
||||
|
||||
('0.5B', 0), # Does it make sense to allow this?
|
||||
('1B', 1),
|
||||
|
||||
('1K', 2**10),
|
||||
('1k', 2**10),
|
||||
('0.5K', 2**9),
|
||||
('2.125K', 2**11 + 2**7),
|
||||
|
||||
('1M', 2**20),
|
||||
('1m', 2**20),
|
||||
('0.5M', 2**19),
|
||||
('2.125M', 2**21 + 2**17),
|
||||
|
||||
('1G', 2**30),
|
||||
('1g', 2**30),
|
||||
('0.25G', 2**28),
|
||||
('2.5G', 2**31 + 2**29),
|
||||
|
||||
('1T', 2**40),
|
||||
('1t', 2**40),
|
||||
('4T', 2**42),
|
||||
('4.125T', 2**42 + 2**37),
|
||||
|
||||
('1P', 2**50),
|
||||
('1P', 2**50),
|
||||
('0.5P', 2**49),
|
||||
('1.5P', 2**50 + 2**49),
|
||||
]
|
||||
|
||||
def test_basics(self):
|
||||
for input, expected_output in self.basics:
|
||||
with self.subTest(input=input):
|
||||
self.assertEqual(expected_output, dehumanize_size(input))
|
||||
|
||||
errors = [
|
||||
('', "input cannot be empty"),
|
||||
('1u', "unrecognized suffix 'u' in '1u'"),
|
||||
('-1', "'-1': cannot be negative"),
|
||||
('1.1.1', "'1.1.1' is not valid input"),
|
||||
('1rm', "'1rm' is not valid input"),
|
||||
('1e6M', "'1e6M' is not valid input"),
|
||||
]
|
||||
|
||||
def test_errors(self):
|
||||
for input, expected_error in self.errors:
|
||||
with self.subTest(input=input):
|
||||
try:
|
||||
dehumanize_size(input)
|
||||
except ValueError as e:
|
||||
actual_error = str(e)
|
||||
else:
|
||||
self.fail("dehumanize_size({!r}) did not error".format(input))
|
||||
self.assertEqual(expected_error, actual_error)
|
|
@ -22,7 +22,7 @@ from subiquitycore.ui.container import ListBox, Pile
|
|||
from subiquitycore.ui.interactive import Selector
|
||||
from subiquitycore.ui.utils import Color, Padding
|
||||
|
||||
from subiquity.models.filesystem import _humanize_size
|
||||
from subiquity.models.filesystem import humanize_size
|
||||
|
||||
log = logging.getLogger('subiquity.ui.bcache')
|
||||
|
||||
|
@ -103,7 +103,7 @@ class BcacheView(BaseView):
|
|||
else:
|
||||
bcachedev = device
|
||||
|
||||
disk_sz = _humanize_size(bcachedev.size)
|
||||
disk_sz = humanize_size(bcachedev.size)
|
||||
disk_string = "{} {}, {}".format(dname,
|
||||
disk_sz,
|
||||
device.model)
|
||||
|
|
|
@ -36,8 +36,8 @@ from subiquitycore.ui.interactive import Selector
|
|||
from subiquitycore.view import BaseView
|
||||
|
||||
from subiquity.models.filesystem import (
|
||||
_humanize_size,
|
||||
_dehumanize_size,
|
||||
humanize_size,
|
||||
dehumanize_size,
|
||||
HUMAN_UNITS,
|
||||
)
|
||||
from subiquity.ui.mount import MountField
|
||||
|
@ -56,7 +56,7 @@ class AddPartitionForm(Form):
|
|||
def __init__(self, model, disk):
|
||||
self.model = model
|
||||
self.disk = disk
|
||||
self.size_str = _humanize_size(disk.free)
|
||||
self.size_str = humanize_size(disk.free)
|
||||
super().__init__()
|
||||
self.size.caption = "Size (max {})".format(self.size_str)
|
||||
self.partnum.value = self.disk.next_partnum
|
||||
|
@ -74,16 +74,15 @@ class AddPartitionForm(Form):
|
|||
v = self.size.value
|
||||
if not v:
|
||||
return
|
||||
r = '(\d+[\.]?\d*)([{}])?$'.format(''.join(HUMAN_UNITS))
|
||||
match = re.match(r, v)
|
||||
if not match:
|
||||
return "Invalid partition size"
|
||||
unit = match.group(2)
|
||||
if unit is None:
|
||||
suffixes = ''.join(HUMAN_UNITS) + ''.join(HUMAN_UNITS).lower()
|
||||
if v[-1] not in suffixes:
|
||||
unit = self.size_str[-1]
|
||||
v += unit
|
||||
self.size.value = v
|
||||
sz = _dehumanize_size(v)
|
||||
try:
|
||||
sz = dehumanize_size(v)
|
||||
except ValueError as v:
|
||||
return str(v)
|
||||
if sz > self.disk.free:
|
||||
self.size.value = self.size_str
|
||||
self.size.show_extra(Color.info_minor(Text("Capped partition size at %s"%(self.size_str,), align="center")))
|
||||
|
@ -128,7 +127,7 @@ class AddPartitionView(BaseView):
|
|||
mount = None
|
||||
|
||||
if self.form.size.value:
|
||||
size = _dehumanize_size(self.form.size.value)
|
||||
size = dehumanize_size(self.form.size.value)
|
||||
if size > self.disk.free:
|
||||
size = self.disk.free
|
||||
else:
|
||||
|
|
|
@ -22,7 +22,7 @@ from subiquitycore.ui.container import Columns, ListBox, Pile
|
|||
from subiquitycore.ui.utils import Padding, Color
|
||||
from subiquitycore.view import BaseView
|
||||
|
||||
from subiquity.models.filesystem import _humanize_size
|
||||
from subiquity.models.filesystem import humanize_size
|
||||
|
||||
|
||||
log = logging.getLogger('subiquity.ui.filesystem.disk_partition')
|
||||
|
@ -58,7 +58,7 @@ class DiskPartitionView(BaseView):
|
|||
|
||||
def format_volume(part):
|
||||
path = part.path
|
||||
size = _humanize_size(part.size)
|
||||
size = humanize_size(part.size)
|
||||
if part.fs() is None:
|
||||
fstype = '-'
|
||||
mountpoint = '-'
|
||||
|
@ -80,7 +80,7 @@ class DiskPartitionView(BaseView):
|
|||
for part in self.disk.partitions():
|
||||
partitioned_disks.append(format_volume(part))
|
||||
if self.disk.free > 0:
|
||||
free_space = _humanize_size(self.disk.free)
|
||||
free_space = humanize_size(self.disk.free)
|
||||
partitioned_disks.append(Columns([
|
||||
(15, Text("FREE SPACE")),
|
||||
Text(free_space),
|
||||
|
@ -134,7 +134,7 @@ class DiskPartitionView(BaseView):
|
|||
text = "Add first partition"
|
||||
if len(self.disk.partitions()) > 0:
|
||||
text = "Add partition (max size {})".format(
|
||||
_humanize_size(self.disk.free))
|
||||
humanize_size(self.disk.free))
|
||||
|
||||
return Color.menu_button(
|
||||
menu_btn(label=text, on_press=self.add_partition))
|
||||
|
|
|
@ -39,7 +39,7 @@ from subiquitycore.ui.container import Columns, ListBox, Pile
|
|||
from subiquitycore.ui.utils import Padding, Color
|
||||
from subiquitycore.view import BaseView
|
||||
|
||||
from subiquity.models.filesystem import _humanize_size
|
||||
from subiquity.models.filesystem import humanize_size
|
||||
|
||||
|
||||
log = logging.getLogger('subiquity.ui.filesystem.filesystem')
|
||||
|
@ -105,10 +105,10 @@ class FilesystemView(BaseView):
|
|||
log.debug('FileSystemView: building part list')
|
||||
cols = []
|
||||
for m in self.model._mounts:
|
||||
cols.append((m.device.volume.path, _humanize_size(m.device.volume.size), m.device.fstype, m.path))
|
||||
cols.append((m.device.volume.path, humanize_size(m.device.volume.size), m.device.fstype, m.path))
|
||||
for fs in self.model._filesystems:
|
||||
if fs.fstype == 'swap':
|
||||
cols.append((fs.volume.path, _humanize_size(fs.volume.size), fs.fstype, 'SWAP'))
|
||||
cols.append((fs.volume.path, humanize_size(fs.volume.size), fs.fstype, 'SWAP'))
|
||||
|
||||
if len(cols) == 0:
|
||||
return Pile([Color.info_minor(
|
||||
|
@ -146,14 +146,14 @@ class FilesystemView(BaseView):
|
|||
disk_btn = menu_btn(label=disk.path)
|
||||
connect_signal(disk_btn, 'click', self.click_disk, disk)
|
||||
col1 = Color.menu_button(disk_btn)
|
||||
col2 = Text(_humanize_size(disk.size))
|
||||
col2 = Text(humanize_size(disk.size))
|
||||
if disk.used > 0:
|
||||
size = disk.size
|
||||
free = disk.free
|
||||
percent = int(100*free/size)
|
||||
if percent == 0:
|
||||
continue
|
||||
col3 = Text("local disk, {} ({}%) free".format(_humanize_size(free), percent))
|
||||
col3 = Text("local disk, {} ({}%) free".format(humanize_size(free), percent))
|
||||
else:
|
||||
col3 = Text("local disk")
|
||||
col(col1, col2, col3)
|
||||
|
@ -166,7 +166,7 @@ class FilesystemView(BaseView):
|
|||
fs = partition.fs().fstype
|
||||
else:
|
||||
fs = "unformatted"
|
||||
col2 = Text(_humanize_size(partition.size))
|
||||
col2 = Text(humanize_size(partition.size))
|
||||
col3 = Text("{} partition on local disk".format(fs))
|
||||
col(col1, col2, col3)
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ from subiquitycore.ui.container import Columns, ListBox, Pile
|
|||
from subiquitycore.ui.interactive import UsernameEditor
|
||||
from subiquitycore.ui.utils import Color, Padding
|
||||
|
||||
from subiquity.models.filesystem import _humanize_size
|
||||
from subiquity.models.filesystem import humanize_size
|
||||
|
||||
|
||||
log = logging.getLogger('subiquitycore.ui.lvm')
|
||||
|
@ -65,7 +65,7 @@ class LVMVolumeGroupView(BaseView):
|
|||
else:
|
||||
lvmdev = device
|
||||
|
||||
disk_sz = _humanize_size(lvmdev.size)
|
||||
disk_sz = humanize_size(lvmdev.size)
|
||||
disk_string = "{} {}, {}".format(dname,
|
||||
disk_sz,
|
||||
device.model)
|
||||
|
|
|
@ -23,7 +23,7 @@ from subiquitycore.ui.interactive import (StringEditor, IntegerEditor,
|
|||
Selector)
|
||||
from subiquitycore.ui.utils import Color, Padding
|
||||
|
||||
from subiquity.models.filesystem import _humanize_size
|
||||
from subiquity.models.filesystem import humanize_size
|
||||
|
||||
log = logging.getLogger('subiquity.ui.raid')
|
||||
|
||||
|
@ -67,7 +67,7 @@ class RaidView(BaseView):
|
|||
else:
|
||||
raiddev = device
|
||||
|
||||
disk_sz = _humanize_size(raiddev.size)
|
||||
disk_sz = humanize_size(raiddev.size)
|
||||
disk_string = "{} {}, {}".format(dname,
|
||||
disk_sz,
|
||||
device.model)
|
||||
|
|
Loading…
Reference in New Issue