From 1cf93a76c2cf307f2e1e514a8944864f746337ea Mon Sep 17 00:00:00 2001 From: Garvit Khatri Date: Tue, 28 Mar 2017 23:43:38 +0800 Subject: [PATCH] bpo-10379: add 'monetary' to format_string, deprecate format Add the 'monetary' parameter to format_string so that all uses of format can be converted to format_string. Adjust the documentation accordingly, and add a deprecation warning when format is used. --- Doc/library/locale.rst | 22 ++++++++++------ Doc/whatsnew/3.7.rst | 56 +++++++++++++++++++++++------------------ Lib/locale.py | 45 ++++++++++++++++++--------------- Lib/test/test_locale.py | 5 ++++ Lib/test/test_types.py | 6 ++--- Misc/NEWS | 3 +++ 6 files changed, 83 insertions(+), 54 deletions(-) diff --git a/Doc/library/locale.rst b/Doc/library/locale.rst index 5aaf4a398f7..181f534abe2 100644 --- a/Doc/library/locale.rst +++ b/Doc/library/locale.rst @@ -352,7 +352,7 @@ The :mod:`locale` module defines the following exception and functions: sequence of strings. -.. function:: format(format, val, grouping=False, monetary=False) +.. function:: format_string(format, val, grouping=False, monetary=False) Formats a number *val* according to the current :const:`LC_NUMERIC` setting. The format follows the conventions of the ``%`` operator. For floating point @@ -362,15 +362,23 @@ The :mod:`locale` module defines the following exception and functions: If *monetary* is true, the conversion uses monetary thousands separator and grouping strings. - Please note that this function will only work for exactly one %char specifier. - For whole format strings, use :func:`format_string`. - - -.. function:: format_string(format, val, grouping=False) - Processes formatting specifiers as in ``format % val``, but takes the current locale settings into account. + .. versionchanged:: 3.7 + The *monetary* keyword parameter was added. + + +.. function:: format(format, val, grouping=False, monetary=False) + + Please note that this function works like format_string but will only work + for exactly one %char specifier. + + For whole format strings, use :func:`format_string`. + + .. deprecated:: 3.7 + Use :meth:`format_string` instead + .. function:: currency(val, symbol=True, grouping=False, international=False) diff --git a/Doc/whatsnew/3.7.rst b/Doc/whatsnew/3.7.rst index b69f452b133..8f0962162c9 100644 --- a/Doc/whatsnew/3.7.rst +++ b/Doc/whatsnew/3.7.rst @@ -76,16 +76,14 @@ Other Language Changes ====================== * More than 255 arguments can now be passed to a function, and a function can - now have more than 255 parameters. - (Contributed by Serhiy Storchaka in :issue:`12844` and :issue:`18896`.) + now have more than 255 parameters. (Contributed by Serhiy Storchaka in + :issue:`12844` and :issue:`18896`.) * :meth:`bytes.fromhex` and :meth:`bytearray.fromhex` now ignore all ASCII - whitespace, not only spaces. - (Contributed by Robert Xiao in :issue:`28927`.) + whitespace, not only spaces. (Contributed by Robert Xiao in :issue:`28927`.) * :exc:`ImportError` now displays module name and module ``__file__`` path when - ``from ... import ...`` fails. - (Contributed by Matthias Bussonnier in :issue:`29546`.) + ``from ... import ...`` fails. (Contributed by Matthias Bussonnier in :issue:`29546`.) New Modules @@ -97,25 +95,32 @@ New Modules Improved Modules ================ +locale +------ + +Added another argument *monetary* in :meth:`format_string` of :mod:`locale`. +If *monetary* is true, the conversion uses monetary thousands separator and +grouping strings. (Contributed by Garvit in :issue:`10379`.) + os -- -Added support for :class:`bytes` paths in :func:`~os.fwalk`. -(Contributed by Serhiy Storchaka in :issue:`28682`.) +Added support for :class:`bytes` paths in :func:`~os.fwalk`. (Contributed by +Serhiy Storchaka in :issue:`28682`.) unittest.mock ------------- The :const:`~unittest.mock.sentinel` attributes now preserve their identity -when they are :mod:`copied ` or :mod:`pickled `. -(Contributed by Serhiy Storchaka in :issue:`20804`.) +when they are :mod:`copied ` or :mod:`pickled `. (Contributed by +Serhiy Storchaka in :issue:`20804`.) xmlrpc.server ------------- :meth:`register_function` of :class:`xmlrpc.server.SimpleXMLRPCDispatcher` and -its subclasses can be used as a decorator. -(Contributed by Xiang Zhang in :issue:`7769`.) +its subclasses can be used as a decorator. (Contributed by Xiang Zhang in +:issue:`7769`.) urllib.parse ------------ @@ -130,13 +135,13 @@ Optimizations * Added two new opcodes: ``LOAD_METHOD`` and ``CALL_METHOD`` to avoid instantiation of bound method objects for method calls, which results - in method calls being faster up to 20%. - (Contributed by Yury Selivanov and INADA Naoki in :issue:`26110`.) + in method calls being faster up to 20%. (Contributed by Yury Selivanov and + INADA Naoki in :issue:`26110`.) * Fast implementation from standard C library is now used for functions :func:`~math.tgamma`, :func:`~math.lgamma`, :func:`~math.erf` and - :func:`~math.erfc` in the :mod:`math` module. - (Contributed by Serhiy Storchaka in :issue:`26121`.) + :func:`~math.erfc` in the :mod:`math` module. (Contributed by Serhiy + Storchaka in :issue:`26121`.) Build and C API Changes @@ -154,8 +159,8 @@ Build and C API Changes ``char *``. (Contributed by Serhiy Storchaka in :issue:`28761`.) * The result of :c:func:`PyUnicode_AsUTF8AndSize` and :c:func:`PyUnicode_AsUTF8` - is now of type ``const char *`` rather of ``char *``. - (Contributed by Serhiy Storchaka in :issue:`28769`.) + is now of type ``const char *`` rather of ``char *``. (Contributed by Serhiy + Storchaka in :issue:`28769`.) * Added functions :c:func:`PySlice_Unpack` and :c:func:`PySlice_AdjustIndices`. (Contributed by Serhiy Storchaka in :issue:`27867`.) @@ -169,6 +174,9 @@ Deprecated ``0x03050400`` and ``0x03060000`` (not including) or ``0x03060100`` or higher. (Contributed by Serhiy Storchaka in :issue:`27867`.) +- Deprecated :meth:`format` from :mod:`locale`, use the :meth:`format_string` + instead. (Contributed by Garvit in :issue:`10379`.) + - Methods :meth:`MetaPathFinder.find_module() ` (replaced by @@ -181,8 +189,8 @@ Deprecated by Matthias Bussonnier in :issue:`29576`) - Using non-integer value for selecting a plural form in :mod:`gettext` is - now deprecated. It never correctly worked. - (Contributed by Serhiy Storchaka in :issue:`28692`.) + now deprecated. It never correctly worked. (Contributed by Serhiy Storchaka + in :issue:`28692`.) Changes in the C API @@ -229,8 +237,8 @@ Changes in the Python API * A format string argument for :meth:`string.Formatter.format` is now :ref:`positional-only `. - Passing it as a keyword argument was deprecated in Python 3.5. - (Contributed by Serhiy Storchaka in :issue:`29193`.) + Passing it as a keyword argument was deprecated in Python 3.5. (Contributed + by Serhiy Storchaka in :issue:`29193`.) * Attributes :attr:`~http.cookies.Morsel.key`, :attr:`~http.cookies.Morsel.value` and @@ -244,8 +252,8 @@ Changes in the Python API ``ClassDef`` AST nodes now have a new ``docstring`` field. The first statement in their body is not considered as a docstring anymore. ``co_firstlineno`` and ``co_lnotab`` of code object for class - and module are affected by this change. - (Contributed by INADA Naoki and Eugene Toder in :issue:`29463`.) + and module are affected by this change. (Contributed by INADA Naoki and + Eugene Toder in :issue:`29463`.) * The *mode* argument of :func:`os.makedirs` no longer affects the file permission bits of newly-created intermediate-level directories. diff --git a/Lib/locale.py b/Lib/locale.py index f8d1d78c36b..50cd6525744 100644 --- a/Lib/locale.py +++ b/Lib/locale.py @@ -17,6 +17,7 @@ import re import collections from builtins import str as _builtin_str import functools +import warnings # Try importing the _locale module. # @@ -180,19 +181,6 @@ def _strip_padding(s, amount): _percent_re = re.compile(r'%(?:\((?P.*?)\))?' r'(?P[-#0-9 +*.hlL]*?)[eEfFgGdiouxXcrs%]') -def format(percent, value, grouping=False, monetary=False, *additional): - """Returns the locale-aware substitution of a %? specifier - (percent). - - additional is for format strings which contain one or more - '*' modifiers.""" - # this is only for one-percent-specifier strings and this should be checked - match = _percent_re.match(percent) - if not match or len(match.group())!= len(percent): - raise ValueError(("format() must be given exactly one %%char " - "format specifier, %s not valid") % repr(percent)) - return _format(percent, value, grouping, monetary, *additional) - def _format(percent, value, grouping=False, monetary=False, *additional): if additional: formatted = percent % ((value,) + additional) @@ -217,10 +205,13 @@ def _format(percent, value, grouping=False, monetary=False, *additional): formatted = _strip_padding(formatted, seps) return formatted -def format_string(f, val, grouping=False): +def format_string(f, val, grouping=False, monetary=False): """Formats a string in the same way that the % formatting would use, but takes the current locale into account. - Grouping is applied if the third parameter is true.""" + + Grouping is applied if the third parameter is true. + Conversion uses monetary thousands separator and grouping strings if + forth parameter monetary is true.""" percents = list(_percent_re.finditer(f)) new_f = _percent_re.sub('%s', f) @@ -230,7 +221,7 @@ def format_string(f, val, grouping=False): if perc.group()[-1]=='%': new_val.append('%') else: - new_val.append(format(perc.group(), val, grouping)) + new_val.append(_format(perc.group(), val, grouping, monetary)) else: if not isinstance(val, tuple): val = (val,) @@ -244,13 +235,27 @@ def format_string(f, val, grouping=False): new_val.append(_format(perc.group(), val[i], grouping, - False, + monetary, *val[i+1:i+1+starcount])) i += (1 + starcount) val = tuple(new_val) return new_f % val +def format(percent, value, grouping=False, monetary=False, *additional): + """Deprecated, use format_string instead.""" + warnings.warn( + "This method will be removed in a future version of Python." + "Use 'locale.format_string()' instead.", + DeprecationWarning, stacklevel=2 + ) + + match = _percent_re.match(percent) + if not match or len(match.group())!= len(percent): + raise ValueError(("format() must be given exactly one %%char " + "format specifier, %s not valid") % repr(percent)) + return _format(percent, value, grouping, monetary, *additional) + def currency(val, symbol=True, grouping=False, international=False): """Formats val according to the currency settings in the current locale.""" @@ -262,7 +267,7 @@ def currency(val, symbol=True, grouping=False, international=False): raise ValueError("Currency formatting is not possible using " "the 'C' locale.") - s = format('%%.%if' % digits, abs(val), grouping, monetary=True) + s = _format('%%.%if' % digits, abs(val), grouping, monetary=True) # '<' and '>' are markers if the sign must be inserted between symbol and value s = '<' + s + '>' @@ -298,7 +303,7 @@ def currency(val, symbol=True, grouping=False, international=False): def str(val): """Convert float to string, taking the locale into account.""" - return format("%.12g", val) + return _format("%.12g", val) def delocalize(string): "Parses a string as a normalized number according to the locale settings." @@ -327,7 +332,7 @@ def atoi(string): def _test(): setlocale(LC_ALL, "") #do grouping - s1 = format("%d", 123456789,1) + s1 = format_string("%d", 123456789,1) print(s1, "is", atoi(s1)) #standard formatting s1 = str(3.14) diff --git a/Lib/test/test_locale.py b/Lib/test/test_locale.py index 99fab58cb93..06d286622a5 100644 --- a/Lib/test/test_locale.py +++ b/Lib/test/test_locale.py @@ -3,6 +3,7 @@ import unittest import locale import sys import codecs +import warnings class BaseLocalizedTest(unittest.TestCase): # @@ -197,6 +198,10 @@ class EnUSNumberFormatting(BaseFormattingTest): self._test_format("%+10.f", -4200, grouping=0, out='-4200'.rjust(10)) self._test_format("%-10.f", 4200, grouping=0, out='4200'.ljust(10)) + def test_format_deprecation(self): + with self.assertWarns(DeprecationWarning): + locale.format("%-10.f", 4200, grouping=True) + def test_complex_formatting(self): # Spaces in formatting string self._test_format_string("One million is %i", 1000000, grouping=1, diff --git a/Lib/test/test_types.py b/Lib/test/test_types.py index 4a9fcba526a..67d3281f3ea 100644 --- a/Lib/test/test_types.py +++ b/Lib/test/test_types.py @@ -381,8 +381,8 @@ class TypesTests(unittest.TestCase): for i in range(-10, 10): x = 1234567890.0 * (10.0 ** i) - self.assertEqual(locale.format('%g', x, grouping=True), format(x, 'n')) - self.assertEqual(locale.format('%.10g', x, grouping=True), format(x, '.10n')) + self.assertEqual(locale.format_string('%g', x, grouping=True), format(x, 'n')) + self.assertEqual(locale.format_string('%.10g', x, grouping=True), format(x, '.10n')) @run_with_locale('LC_NUMERIC', 'en_US.UTF8') def test_int__format__locale(self): @@ -390,7 +390,7 @@ class TypesTests(unittest.TestCase): x = 123456789012345678901234567890 for i in range(0, 30): - self.assertEqual(locale.format('%d', x, grouping=True), format(x, 'n')) + self.assertEqual(locale.format_string('%d', x, grouping=True), format(x, 'n')) # move to the next integer to test x = x // 10 diff --git a/Misc/NEWS b/Misc/NEWS index 024b31ded4c..eceee8d62ab 100644 --- a/Misc/NEWS +++ b/Misc/NEWS @@ -397,6 +397,9 @@ Library - bpo-29534: Fixed different behaviour of Decimal.from_float() for _decimal and _pydecimal. Thanks Andrew Nester. +- bpo-10379: locale.format_string now supports the 'monetary' keyword argument, + and locale.format is deprecated. + - Issue #28556: Various updates to typing module: typing.Counter, typing.ChainMap, improved ABC caching, etc. Original PRs by Jelle Zijlstra, Ivan Levkivskyi, Manuel Krebber, and Ɓukasz Langa.