blivet.size.Size expects size spec strings to be specified in the current language, so we need to translate all the constants we use with "whatever MB".
There are still going to be troubles if anyone completes and attempts to use a blivet translation with a non-Latin charset, and we don't allow decimal separators that aren't '.' (related: python is seriously bad at that kind of thing), but at least this way everything can be fixed in one spot.
Size specs are expected to be translated to the current locale, so translate constant size specs before passing them to the Size constructor. --- blivet/__init__.py | 12 ++++++------ blivet/size.py | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 6 deletions(-)
diff --git a/blivet/__init__.py b/blivet/__init__.py index dbd8b69..327389b 100644 --- a/blivet/__init__.py +++ b/blivet/__init__.py @@ -85,7 +85,7 @@ import util import arch from flags import flags from platform import platform as _platform -from size import Size +from size import Size, xlate_size
import shelve import contextlib @@ -876,7 +876,7 @@ class Blivet(object): should_clear = self.shouldClear(disk, clearPartType=clearPartType, clearPartDisks=[disk.name]) if should_clear: - free[disk.name] = (Size(spec="%f mb" % disk.size), 0) + free[disk.name] = (Size(spec=xlate_size("%f mb" % disk.size)), 0) continue
disk_free = 0 @@ -899,8 +899,8 @@ class Blivet(object): elif disk.format.type is None: disk_free = disk.size
- free[disk.name] = (Size(spec="%f mb" % disk_free), - Size(spec="%f mb" % fs_free)) + free[disk.name] = (Size(spec=xlate_size("%f mb" % disk_free)), + Size(spec=xlate_size("%f mb" % fs_free)))
return free
@@ -1595,8 +1595,8 @@ class Blivet(object): "'biosboot' type partition."))
if not swaps: - installed = Size(spec="%s kb" % util.total_memory()) - required = Size(spec="%s kb" % isys.EARLY_SWAP_RAM) + installed = Size(spec=xlate_size("%s kb" % util.total_memory())) + required = Size(spec=xlate_size("%s kb" % isys.EARLY_SWAP_RAM))
if installed < required: errors.append(_("You have not specified a swap partition. " diff --git a/blivet/size.py b/blivet/size.py index 6c3eb90..d9af722 100644 --- a/blivet/size.py +++ b/blivet/size.py @@ -99,6 +99,22 @@ def _parseSpec(spec):
raise ValueError("invalid size specification", spec)
+def xlate_size(spec): + """Translate English size specifiers into the current locale.""" + + m = re.match(r'(.*?)([A-Za-z]*)$', spec.strip()) + if not m: + raise ValueError("invalid size specification", spec) + + specifier = m.groups()[1].lower() + + # Look for MB, megabyte, megabytes, translate prefix and byte separately + for ending in ['byte', 'bytes', 'b']: + if specifier.endswith(ending): + return m.groups()[0] + _(specifier[:-len(ending)]) + _(ending) + else: + return m.groups()[0] + _(specifier) + class Size(Decimal): """ Common class to represent storage device and filesystem sizes. Can handle parsing strings such as 45MB or 6.7GB to initialize
On Wed, 2013-11-20 at 15:29 -0500, David Shea wrote:
Size specs are expected to be translated to the current locale, so translate constant size specs before passing them to the Size constructor.
blivet/__init__.py | 12 ++++++------ blivet/size.py | 16 ++++++++++++++++ 2 files changed, 22 insertions(+), 6 deletions(-)
diff --git a/blivet/__init__.py b/blivet/__init__.py index dbd8b69..327389b 100644 --- a/blivet/__init__.py +++ b/blivet/__init__.py @@ -85,7 +85,7 @@ import util import arch from flags import flags from platform import platform as _platform -from size import Size +from size import Size, xlate_size
import shelve import contextlib @@ -876,7 +876,7 @@ class Blivet(object): should_clear = self.shouldClear(disk, clearPartType=clearPartType, clearPartDisks=[disk.name]) if should_clear:
free[disk.name] = (Size(spec="%f mb" % disk.size), 0)
free[disk.name] = (Size(spec=xlate_size("%f mb" % disk.size)), 0) continue disk_free = 0
@@ -899,8 +899,8 @@ class Blivet(object): elif disk.format.type is None: disk_free = disk.size
free[disk.name] = (Size(spec="%f mb" % disk_free),
Size(spec="%f mb" % fs_free))
free[disk.name] = (Size(spec=xlate_size("%f mb" % disk_free)),
Size(spec=xlate_size("%f mb" % fs_free))) return free
@@ -1595,8 +1595,8 @@ class Blivet(object): "'biosboot' type partition."))
if not swaps:
installed = Size(spec="%s kb" % util.total_memory())
required = Size(spec="%s kb" % isys.EARLY_SWAP_RAM)
installed = Size(spec=xlate_size("%s kb" % util.total_memory()))
required = Size(spec=xlate_size("%s kb" % isys.EARLY_SWAP_RAM)) if installed < required: errors.append(_("You have not specified a swap partition. "
diff --git a/blivet/size.py b/blivet/size.py index 6c3eb90..d9af722 100644 --- a/blivet/size.py +++ b/blivet/size.py @@ -99,6 +99,22 @@ def _parseSpec(spec):
raise ValueError("invalid size specification", spec)
+def xlate_size(spec):
- """Translate English size specifiers into the current locale."""
- m = re.match(r'(.*?)([A-Za-z]*)$', spec.strip())
I belive this RE deserves a comment about the .*? combination.
--- pyanaconda/packaging/dnfpayload.py | 4 ++-- pyanaconda/packaging/yumpayload.py | 4 ++-- pyanaconda/ui/gui/spokes/custom.py | 26 +++++++++++++------------- pyanaconda/ui/gui/spokes/lib/accordion.py | 4 ++-- pyanaconda/ui/gui/spokes/lib/resize.py | 8 ++++---- pyanaconda/ui/gui/spokes/storage.py | 8 ++++---- pyanaconda/ui/lib/disks.py | 4 ++-- pyanaconda/ui/lib/space.py | 4 ++-- pyanaconda/ui/tui/spokes/storage.py | 4 ++-- 9 files changed, 33 insertions(+), 33 deletions(-)
diff --git a/pyanaconda/packaging/dnfpayload.py b/pyanaconda/packaging/dnfpayload.py index 9de7ceb..a61fac4 100644 --- a/pyanaconda/packaging/dnfpayload.py +++ b/pyanaconda/packaging/dnfpayload.py @@ -20,7 +20,7 @@ # Red Hat Author(s): Ales Kozumplik akozumpl@redhat.com #
-from blivet.size import Size +from blivet.size import Size, xlate_size from pyanaconda.flags import flags from pyanaconda.i18n import _ from pyanaconda.progress import progressQ @@ -248,7 +248,7 @@ class DNFPayload(packaging.PackagePayload): def spaceRequired(self): transaction = self._base.transaction if transaction is None: - return Size(spec="3000 MB") + return Size(spec=xlate_size("3000 MB"))
size = sum(tsi.installed.installsize for tsi in transaction) # add 35% to account for the fact that the above method is laughably diff --git a/pyanaconda/packaging/yumpayload.py b/pyanaconda/packaging/yumpayload.py index c82fc94..aac5449 100644 --- a/pyanaconda/packaging/yumpayload.py +++ b/pyanaconda/packaging/yumpayload.py @@ -70,7 +70,7 @@ from pyanaconda.iutil import ProxyString, ProxyStringError from pyanaconda.i18n import _ from pyanaconda.nm import nm_is_connected from pyanaconda.product import productName, isFinal -from blivet.size import Size +from blivet.size import Size, xlate_size import blivet.util import blivet.arch
@@ -193,7 +193,7 @@ class YumPayload(PackagePayload): # This value comes from a default install of the x86_64 Fedora 18. It # is meant as a best first guess only. Once package metadata is # available we can use that as a better value. - self._space_required = Size(spec="3000 MB") + self._space_required = Size(spec=xlate_size("3000 MB"))
self._groups = None self._packages = [] diff --git a/pyanaconda/ui/gui/spokes/custom.py b/pyanaconda/ui/gui/spokes/custom.py index 1950a5b..4e135ef 100644 --- a/pyanaconda/ui/gui/spokes/custom.py +++ b/pyanaconda/ui/gui/spokes/custom.py @@ -46,7 +46,7 @@ from blivet.formats import device_formats from blivet.formats import getFormat from blivet.formats.fs import FS from blivet.platform import platform -from blivet.size import Size +from blivet.size import Size, xlate_size from blivet import Root from blivet.devicefactory import DEVICE_TYPE_LVM from blivet.devicefactory import DEVICE_TYPE_BTRFS @@ -152,7 +152,7 @@ def size_from_entry(entry):
# if no unit was specified, default to MB if not re.search(r'[A-Za-z]+$', size_text): - size_text += "MB" + size_text += xlate_size("MB")
try: size = Size(spec=size_text) @@ -160,8 +160,8 @@ def size_from_entry(entry): return None else: # Minimium size for ui-created partitions is 1MB. - if size.convertTo(spec="mb") < 1: - size = Size(spec="1mb") + if size.convertTo(spec=xlate_size("mb")) < 1: + size = Size(spec=xlate_size("1mb"))
return size
@@ -352,7 +352,7 @@ class DisksDialog(GUIObject): # populate the store for disk in self._disks: self._store.append([disk.description, - str(Size(spec="%dMB" % disk.size)), + str(Size(spec=xlate_size("%dMB" % disk.size))), str(free[disk.name][0]), disk.serial, disk.id]) @@ -436,7 +436,7 @@ class ContainerDialog(GUIObject): # populate the store for disk in self._disks: self._store.append([disk.description, - str(Size(spec="%dMB" % disk.size)), + str(Size(spec=xlate_size("%dMB" % disk.size))), str(free[disk.name][0]), disk.serial, disk.id]) @@ -462,7 +462,7 @@ class ContainerDialog(GUIObject): self._raidStoreFilter.refilter() self._populate_raid()
- size = Size(spec="%d mb" % self.size) + size = Size(spec=xlate_size("%d mb" % self.size)) self._sizeEntry.set_text(size.humanReadable(max_places=None)) if self.size_policy == SIZE_POLICY_AUTO: self._sizeCombo.set_active(0) @@ -538,7 +538,7 @@ class ContainerDialog(GUIObject): elif idx == 2: size = size_from_entry(self._sizeEntry) if size: - size = int(size.convertTo(spec="MB")) + size = int(size.convertTo(spec=xlate_size("MB"))) elif size is None: size = SIZE_POLICY_MAX
@@ -832,7 +832,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker): for disk in self._clearpartDevices: totalSpace += disk.size
- return Size(spec="%f MB" % totalSpace) + return Size(spec=xlate_size("%f MB" % totalSpace))
def _updateSpaceDisplay(self): # Set up the free space/available space displays in the bottom left. @@ -1145,7 +1145,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker): old_size = device.size size = size_from_entry(self._sizeEntry) if size: - size = int(size.convertTo(spec="MB")) + size = int(size.convertTo(spec=xlate_size("MB"))) changed_size = ((use_dev.resizable or not use_dev.exists) and size != int(old_size)) log.debug("old size: %s", old_size) @@ -1697,7 +1697,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker): else: self._labelEntry.set_tooltip_text(_("This file system does not support labels."))
- self._sizeEntry.set_text(Size(spec="%d MB" % device.size).humanReadable(max_places=None)) + self._sizeEntry.set_text(Size(spec=xlate_size("%d MB" % device.size)).humanReadable(max_places=None))
self._reformatCheckbox.set_active(not device.format.exists) fancy_set_sensitive(self._reformatCheckbox, not device.protected and @@ -1945,7 +1945,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker): log.debug("requested size = %s ; available space = %s", dialog.size, self._free_space)
# if no size was entered, request as much of the free space as possible - if dialog.size is not None and dialog.size.convertTo(spec="mb") < 1: + if dialog.size is not None and dialog.size.convertTo(spec=xlate_size("mb")) < 1: size = None else: size = dialog.size @@ -1989,7 +1989,7 @@ class CustomPartitioningSpoke(NormalSpoke, StorageChecker):
disks = self._clearpartDevices if size is not None: - size = float(size.convertTo(spec="mb")) + size = float(size.convertTo(spec=xlate_size("mb")))
self.clear_errors()
diff --git a/pyanaconda/ui/gui/spokes/lib/accordion.py b/pyanaconda/ui/gui/spokes/lib/accordion.py index 5990332..5f435e8 100644 --- a/pyanaconda/ui/gui/spokes/lib/accordion.py +++ b/pyanaconda/ui/gui/spokes/lib/accordion.py @@ -20,7 +20,7 @@ # Red Hat Author(s): Chris Lumens clumens@redhat.com #
-from blivet.size import Size +from blivet.size import Size, xlate_size
from pyanaconda.i18n import _ from pyanaconda.product import productName, productVersion @@ -58,7 +58,7 @@ def selectorFromDevice(device, selector=None, mountpoint=""): else: mp = _("Unknown")
- size = Size(spec="%f MB" % device.size) + size = Size(spec=xlate_size("%f MB" % device.size))
if not selector: selector = MountpointSelector(device.name, str(size).upper(), mp) diff --git a/pyanaconda/ui/gui/spokes/lib/resize.py b/pyanaconda/ui/gui/spokes/lib/resize.py index f1b6905..9cb4251 100644 --- a/pyanaconda/ui/gui/spokes/lib/resize.py +++ b/pyanaconda/ui/gui/spokes/lib/resize.py @@ -28,7 +28,7 @@ from pyanaconda.i18n import _, N_, P_ from pyanaconda.ui.lib.disks import size_str from pyanaconda.ui.gui import GUIObject from pyanaconda.ui.gui.utils import timed_action -from blivet.size import Size +from blivet.size import Size, xlate_size
__all__ = ["ResizeDialog"]
@@ -173,7 +173,7 @@ class ResizeDialog(GUIObject): # And then add another uneditable line that lists how much space is # already free in the disk. diskFree = free_space[disk.name][0] - converted = diskFree.convertTo(spec="mb") + converted = diskFree.convertTo(spec=xlate_size("mb")) if int(converted): self._diskStore.append(itr, [disk.id, _("""<span foreground='grey' style='italic'>Free space</span>"""), @@ -375,7 +375,7 @@ class ResizeDialog(GUIObject): self._diskStore.foreach(self._sumReclaimableSpace, None) self._update_labels(selectedReclaimable=self._selectedReclaimableSpace)
- self._update_reclaim_button(Size(spec="%f MB" % self._selectedReclaimableSpace)) + self._update_reclaim_button(Size(spec=xlate_size("%f MB" % self._selectedReclaimableSpace))) self._update_action_buttons(selectedRow)
def _scheduleActions(self, model, path, itr, *args): @@ -458,7 +458,7 @@ class ResizeDialog(GUIObject): self._update_labels(selectedReclaimable=self._selectedReclaimableSpace)
# And then the reclaim button, in case they've made enough space. - self._update_reclaim_button(Size(spec="%f MB" % self._selectedReclaimableSpace)) + self._update_reclaim_button(Size(spec=xlate_size("%f MB" % self._selectedReclaimableSpace)))
def resize_slider_format(self, scale, value): # This makes the value displayed under the slider prettier than just a diff --git a/pyanaconda/ui/gui/spokes/storage.py b/pyanaconda/ui/gui/spokes/storage.py index 01cf18e..b20c4f7 100644 --- a/pyanaconda/ui/gui/spokes/storage.py +++ b/pyanaconda/ui/gui/spokes/storage.py @@ -53,7 +53,7 @@ from pyanaconda.ui.gui.utils import enlightbox
from pyanaconda.kickstart import doKickstartStorage, getAvailableDiskSpace from blivet import empty_device -from blivet.size import Size +from blivet.size import Size, xlate_size from blivet.devices import MultipathDevice from blivet.errors import StorageError from blivet.platform import platform @@ -711,7 +711,7 @@ class StorageSpoke(NormalSpoke, StorageChecker): summary = (P_("%(count)d disk selected; %(capacity)s capacity; %(free)s free", "%(count)d disks selected; %(capacity)s capacity; %(free)s free", count) % {"count" : count, - "capacity" : str(Size(spec="%f MB" % capacity)), + "capacity" : str(Size(spec=xlate_size("%f MB" % capacity))), "free" : free}) summary_label = self.builder.get_object("summary_label") summary_label.set_text(summary) @@ -814,7 +814,7 @@ class StorageSpoke(NormalSpoke, StorageChecker):
# show the installation options dialog disks = [d for d in self.disks if d.name in self.selected_disks] - disks_size = sum(Size(spec="%f MB" % d.size) for d in disks) + disks_size = sum(Size(spec=xlate_size("%f MB" % d.size)) for d in disks)
# No disks selected? The user wants to back out of the storage spoke. if not disks: @@ -842,7 +842,7 @@ class StorageSpoke(NormalSpoke, StorageChecker): auto_swap = Size(bytes=0) for autoreq in self.storage.autoPartitionRequests: if autoreq.fstype == "swap": - auto_swap += Size(spec="%d MB" % autoreq.size) + auto_swap += Size(spec=xlate_size("%d MB" % autoreq.size))
log.debug("disk free: %s fs free: %s sw needs: %s auto swap: %s", disk_free, fs_free, required_space, auto_swap) diff --git a/pyanaconda/ui/lib/disks.py b/pyanaconda/ui/lib/disks.py index 4dcd701..94c89e7 100644 --- a/pyanaconda/ui/lib/disks.py +++ b/pyanaconda/ui/lib/disks.py @@ -21,7 +21,7 @@ #
from blivet.devices import MultipathDevice, iScsiDiskDevice -from blivet.size import Size +from blivet.size import Size, xlate_size
from pyanaconda.flags import flags
@@ -77,6 +77,6 @@ def size_str(mb): if isinstance(mb, Size): spec = str(mb) else: - spec = "%f mb" % mb + spec = xlate_size("%f mb" % mb)
return str(Size(spec=spec)).upper() diff --git a/pyanaconda/ui/lib/space.py b/pyanaconda/ui/lib/space.py index 0c252d1..d9c6e46 100644 --- a/pyanaconda/ui/lib/space.py +++ b/pyanaconda/ui/lib/space.py @@ -19,7 +19,7 @@ # Red Hat Author(s): David Lehman dlehman@redhat.com #
-from blivet.size import Size +from blivet.size import Size, xlate_size
from pyanaconda.i18n import _, N_
@@ -71,7 +71,7 @@ class FileSystemSpaceChecker(object): in the info bar at the bottom of a Hub. """ self.reset() - free = Size(spec="%.2f MB" % self.storage.fileSystemFreeSpace) + free = Size(spec=xlate_size("%.2f MB" % self.storage.fileSystemFreeSpace)) needed = self.payload.spaceRequired log.info("fs space: %s needed: %s", free, needed) self.success = (free >= needed) diff --git a/pyanaconda/ui/tui/spokes/storage.py b/pyanaconda/ui/tui/spokes/storage.py index 0936b5f..2870853 100644 --- a/pyanaconda/ui/tui/spokes/storage.py +++ b/pyanaconda/ui/tui/spokes/storage.py @@ -27,7 +27,7 @@ from pyanaconda.ui.tui.spokes import NormalTUISpoke from pyanaconda.ui.tui.simpleline import TextWidget, CheckboxWidget
from pykickstart.constants import AUTOPART_TYPE_LVM, AUTOPART_TYPE_BTRFS, AUTOPART_TYPE_PLAIN -from blivet.size import Size +from blivet.size import Size, xlate_size from blivet.errors import StorageError from pyanaconda.flags import flags from pyanaconda.kickstart import doKickstartStorage @@ -155,7 +155,7 @@ class StorageSpoke(NormalTUISpoke):
summary = (P_(("%d disk selected; %s capacity; %s free ..."), ("%d disks selected; %s capacity; %s free ..."), - count) % (count, str(Size(spec="%f MB" % capacity)), free)) + count) % (count, str(Size(spec=xlate_size("%f MB" % capacity))), free))
if len(self.disks) == 0: summary = _("No disks detected. Please shut down the computer, connect at least one disk, and restart to complete installation.")
On Wed, 2013-11-20 at 15:29 -0500, David Shea wrote:
pyanaconda/packaging/dnfpayload.py | 4 ++-- pyanaconda/packaging/yumpayload.py | 4 ++-- pyanaconda/ui/gui/spokes/custom.py | 26 +++++++++++++------------- pyanaconda/ui/gui/spokes/lib/accordion.py | 4 ++-- pyanaconda/ui/gui/spokes/lib/resize.py | 8 ++++---- pyanaconda/ui/gui/spokes/storage.py | 8 ++++---- pyanaconda/ui/lib/disks.py | 4 ++-- pyanaconda/ui/lib/space.py | 4 ++-- pyanaconda/ui/tui/spokes/storage.py | 4 ++-- 9 files changed, 33 insertions(+), 33 deletions(-)
Is there any reason for not translating specs in the the Size class' constructor and methods? Maybe with adding keyword argument xlate=True to those methods?
--- blivet/size.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/blivet/size.py b/blivet/size.py index d9af722..7c03c2e 100644 --- a/blivet/size.py +++ b/blivet/size.py @@ -75,7 +75,11 @@ def _parseSpec(spec): if not spec: raise ValueError("invalid size specification", spec)
- m = re.match(r'(-?\s*[0-9.]+)\s*([A-Za-z]*)$', spec.strip()) + # This regex isn't ideal, since \w matches both letters and digits, + # but python doesn't provide a means to match only Unicode letters. + # Probably the worst that will come of it is that bad specs will fail + # more confusingly. + m = re.match(r'(-?\s*[0-9.]+)\s*(\w*)$', spec.decode("utf-8").strip(), flags=re.UNICODE) if not m: raise ValueError("invalid size specification", spec)
@@ -102,6 +106,8 @@ def _parseSpec(spec): def xlate_size(spec): """Translate English size specifiers into the current locale."""
+ # Keep the non-unicode A-Za-z here since the input is English and + # this lets us be lazier about matching the non-specifier part m = re.match(r'(.*?)([A-Za-z]*)$', spec.strip()) if not m: raise ValueError("invalid size specification", spec)
anaconda-patches@lists.fedorahosted.org