From 79fe4a5dbbacecfca0961e228911fefaf6a9087b Mon Sep 17 00:00:00 2001 From: JarbasAI <33701864+JarbasAl@users.noreply.github.com> Date: Sun, 9 May 2021 12:17:38 +0100 Subject: [PATCH] feat/nice_bytes (#19) Co-authored-by: jarbasal --- lingua_nostra/format.py | 50 ++++++++++++++ lingua_nostra/internal.py | 8 +++ test/test_format.py | 133 +++++++++++++++++++++++++++++++++++++- 3 files changed, 189 insertions(+), 2 deletions(-) diff --git a/lingua_nostra/format.py b/lingua_nostra/format.py index e46caf03..52b653ec 100755 --- a/lingua_nostra/format.py +++ b/lingua_nostra/format.py @@ -566,3 +566,53 @@ def nice_response(text, lang=''): assertEqual(nice_response_de("10 ^ 2"), "10 hoch 2") """ + + +@localized_function(run_own_code_on=[FunctionNotLocalizedError]) +def nice_bytes(number, lang='', speech=True, binary=True, gnu=False): + """ + turns a number of bytes into a string using appropriate units + prefixes - https://en.wikipedia.org/wiki/Binary_prefix + spoken binary units - https://en.wikipedia.org/wiki/Kibibyte + implementation - http://stackoverflow.com/a/1094933/2444609 + :param number: number of bytes (int) + :param lang: lang_code, ignored for now (str) + :param speech: spoken form (True) or short units (False) + :param binary: 1 kilobyte = 1024 bytes (True) or 1 kilobyte = 1000 bytes (False) + :param gnu: say only order of magnitude (bool) - 100 Kilo (True) or 100 Kilobytes (False) + :return: nice bytes (str) + """ + if speech and gnu: + default_units = ['Bytes', 'Kilo', 'Mega', 'Giga', 'Tera', 'Peta', + 'Exa', 'Zetta', 'Yotta'] + elif speech and binary: + default_units = ['Bytes', 'Kibibytes', 'Mebibytes', 'Gibibytes', + 'Tebibytes', 'Pebibytes', 'Exbibytes', 'Zebibytes', + 'Yobibytes'] + elif speech: + default_units = ['Bytes', 'Kilobytes', 'Megabytes', 'Gigabytes', + 'Terabytes', 'Petabytes', 'Exabytes', 'Zettabytes', + 'Yottabytes'] + elif gnu: + default_units = ['B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'] + elif binary: + default_units = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', + 'YiB'] + else: + default_units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] + + units = default_units + + if binary: + n = 1024 + else: + n = 1000 + + for unit in units[:-1]: + if abs(number) < n: + if number == 1 and speech and not gnu: + # strip final "s" + unit = unit[:-1] + return "%3.1f %s" % (number, unit) + number /= n + return "%.1f %s" % (number, units[-1]) diff --git a/lingua_nostra/internal.py b/lingua_nostra/internal.py index b551eca4..0f1ea5c7 100644 --- a/lingua_nostra/internal.py +++ b/lingua_nostra/internal.py @@ -559,6 +559,14 @@ def _call_localized_function(func, *args, **kwargs): # If we didn't find a localized function to correspond with # the wrapped function, we cached NotImplementedError in its # place. + + # first account for the function not being present in any + # module, meaning all modules are falling back to a catch all + # parser, this usually means the function will need localization + # only in future languages not currently supported + if func_name not in _localized_functions[_module_name][lang_code]: + raise FunctionNotLocalizedError(func_name, lang_code) + loc_signature = _localized_functions[_module_name][lang_code][func_name] if isinstance(loc_signature, type(NotImplementedError())): raise loc_signature diff --git a/test/test_format.py b/test/test_format.py index 00488f3b..5796cc69 100644 --- a/test/test_format.py +++ b/test/test_format.py @@ -35,6 +35,7 @@ from lingua_nostra.format import pronounce_number from lingua_nostra.format import date_time_format from lingua_nostra.format import join_list +from lingua_nostra.format import nice_bytes def setUpModule(): @@ -80,7 +81,6 @@ def tearDownModule(): class TestNiceNumberFormat(unittest.TestCase): - tmp_var = None def set_tmp_var(self, val): @@ -116,6 +116,7 @@ def test_unknown_language(self): """ An unknown / unhandled language should return the string representation of the input number. """ + def bypass_warning(): self.assertEqual( nice_number(5.5, lang='as-df'), '5.5', @@ -384,6 +385,7 @@ def test_ordinals(self): short_scale=False), "eighteen " "trillionth") + # def nice_time(dt, lang="en-us", speech=True, use_24hour=False, # use_ampm=False): @@ -600,7 +602,7 @@ def test_nice_year(self): self.assertTrue(len(nice_year(dt, lang=lang)) > 0) # Looking through the date sequence can be helpful -# print(nice_year(dt, lang=lang)) + # print(nice_year(dt, lang=lang)) def test_nice_duration(self): self.assertEqual(nice_duration(1), "one second") @@ -637,5 +639,132 @@ def test_join(self): self.assertEqual(join_list([1, "b", 3, "d"], "or"), "1, b, 3 or d") +class TestNiceBytes(unittest.TestCase): + + def test_nice_bytes_non_binary_speech(self): + self.assertEqual(nice_bytes(0, binary=False), "0.0 Bytes") + self.assertEqual(nice_bytes(1, binary=False), "1.0 Byte") + self.assertEqual(nice_bytes(1000, binary=False), "1.0 Kilobyte") + self.assertEqual(nice_bytes(2000000, binary=False), "2.0 Megabytes") + self.assertEqual(nice_bytes(2000000000, binary=False), "2.0 Gigabytes") + self.assertEqual(nice_bytes(2000000000000, binary=False), + "2.0 Terabytes") + self.assertEqual(nice_bytes(2000000000000000, binary=False), + "2.0 Petabytes") + self.assertEqual(nice_bytes(2000000000000000000, binary=False), + "2.0 Exabytes") + self.assertEqual(nice_bytes(2000000000000000000000, binary=False), + "2.0 Zettabytes") + self.assertEqual(nice_bytes(2000000000000000000000000, binary=False), + "2.0 Yottabytes") + # no more named prefixes after Y - https://en.wikipedia.org/wiki/Binary_prefix + self.assertEqual( + nice_bytes(2000000000000000000000000000, binary=False), + "2000.0 Yottabytes") + + def test_nice_bytes_non_binary(self): + self.assertEqual(nice_bytes(0, speech=False, binary=False), "0.0 B") + self.assertEqual(nice_bytes(1000, speech=False, binary=False), + "1.0 KB") + self.assertEqual(nice_bytes(1024, speech=False, binary=False), + "1.0 KB") + self.assertEqual(nice_bytes(2000000, speech=False, binary=False), + "2.0 MB") + self.assertEqual(nice_bytes(2000000000, speech=False, binary=False), + "2.0 GB") + self.assertEqual(nice_bytes(2000000000000, speech=False, binary=False), + "2.0 TB") + self.assertEqual( + nice_bytes(2000000000000000, speech=False, binary=False), "2.0 PB") + self.assertEqual( + nice_bytes(2000000000000000000, speech=False, binary=False), + "2.0 EB") + self.assertEqual( + nice_bytes(2000000000000000000000, speech=False, binary=False), + "2.0 ZB") + self.assertEqual( + nice_bytes(2000000000000000000000000, speech=False, binary=False), + "2.0 YB") + # no more named prefixes after Y - https://en.wikipedia.org/wiki/Binary_prefix + self.assertEqual(nice_bytes(2000000000000000000000000000, speech=False, + binary=False), "2000.0 YB") + + def test_nice_bytes_speech(self): + # https://en.wikipedia.org/wiki/Kibibyte + self.assertEqual(nice_bytes(0), "0.0 Bytes") + self.assertEqual(nice_bytes(1), "1.0 Byte") + self.assertEqual(nice_bytes(1000), "1000.0 Bytes") + self.assertEqual(nice_bytes(1024), "1.0 Kibibyte") + self.assertEqual(nice_bytes(2000000), "1.9 Mebibytes") + self.assertEqual(nice_bytes(2000000000), "1.9 Gibibytes") + self.assertEqual(nice_bytes(2000000000000), "1.8 Tebibytes") + self.assertEqual(nice_bytes(2000000000000000), "1.8 Pebibytes") + self.assertEqual(nice_bytes(2000000000000000000), "1.7 Exbibytes") + self.assertEqual(nice_bytes(2000000000000000000000), "1.7 Zebibytes") + self.assertEqual(nice_bytes(2000000000000000000000000), + "1.7 Yobibytes") + # no more named prefixes after Y - https://en.wikipedia.org/wiki/Binary_prefix + self.assertEqual(nice_bytes(2000000000000000000000000000), + "1654.4 Yobibytes") + + def test_nice_bytes(self): + self.assertEqual(nice_bytes(0, speech=False), "0.0 B") + self.assertEqual(nice_bytes(1000, speech=False), "1000.0 B") + self.assertEqual(nice_bytes(1024, speech=False), "1.0 KiB") + self.assertEqual(nice_bytes(2000000, speech=False), "1.9 MiB") + self.assertEqual(nice_bytes(2000000000, speech=False), "1.9 GiB") + self.assertEqual(nice_bytes(2000000000000, speech=False), "1.8 TiB") + self.assertEqual(nice_bytes(2000000000000000, speech=False), "1.8 PiB") + self.assertEqual(nice_bytes(2000000000000000000, speech=False), + "1.7 EiB") + self.assertEqual(nice_bytes(2000000000000000000000, speech=False), + "1.7 ZiB") + self.assertEqual(nice_bytes(2000000000000000000000000, speech=False), + "1.7 YiB") + # no more named prefixes after Y - https://en.wikipedia.org/wiki/Binary_prefix + self.assertEqual( + nice_bytes(2000000000000000000000000000, speech=False), + "1654.4 YiB") + + def test_nice_bytes_gnu(self): + self.assertEqual(nice_bytes(1024, gnu=True), "1.0 Kilo") + self.assertEqual(nice_bytes(2000000, gnu=True), "1.9 Mega") + self.assertEqual(nice_bytes(2000000000, gnu=True), "1.9 Giga") + self.assertEqual(nice_bytes(2000000000000, gnu=True), "1.8 Tera") + + self.assertEqual(nice_bytes(0, speech=False, gnu=True), "0.0 B") + self.assertEqual(nice_bytes(1000, speech=False, gnu=True), "1000.0 B") + self.assertEqual(nice_bytes(1024, speech=False, gnu=True), "1.0 K") + self.assertEqual(nice_bytes(2000000, speech=False, gnu=True), "1.9 M") + self.assertEqual(nice_bytes(2000000000, speech=False, gnu=True), + "1.9 G") + self.assertEqual(nice_bytes(2000000000000, speech=False, gnu=True), + "1.8 T") + self.assertEqual(nice_bytes(2000000000000000, speech=False, gnu=True), + "1.8 P") + self.assertEqual( + nice_bytes(2000000000000000000, speech=False, gnu=True), "1.7 E") + self.assertEqual( + nice_bytes(2000000000000000000000, speech=False, gnu=True), + "1.7 Z") + self.assertEqual( + nice_bytes(2000000000000000000000000, speech=False, gnu=True), + "1.7 Y") + # no more named prefixes after Y - https://en.wikipedia.org/wiki/Binary_prefix + self.assertEqual( + nice_bytes(2000000000000000000000000000, speech=False, gnu=True), + "1654.4 Y") + + self.assertEqual(nice_bytes(2000000, gnu=True, binary=False), + "2.0 Mega") + self.assertEqual(nice_bytes(2000000000, gnu=True, binary=False), + "2.0 Giga") + self.assertEqual( + nice_bytes(2000000, speech=False, gnu=True, binary=False), "2.0 M") + self.assertEqual( + nice_bytes(2000000000, speech=False, gnu=True, binary=False), + "2.0 G") + + if __name__ == "__main__": unittest.main()