jenkins-bot has submitted this change. ( 
https://gerrit.wikimedia.org/r/c/pywikibot/core/+/789191 )

Change subject: [bugfix] do not use old color format for transliterated chars
......................................................................

[bugfix] do not use old color format for transliterated chars

- do not use old color format for transliterated chars
- fix other occurences of old color format
- deprecate the old color format. There are to many problems if keeping
  both variants.
- use new color format with deprecated color_format function
- update tests accordingly
- remove unused _ColorFormatter class

Bug: T307550
Change-Id: I811d85ab7205f43da798d42c72ff81283a5ccecd
---
M pywikibot/bot_choice.py
M pywikibot/tools/formatter.py
M pywikibot/userinterfaces/terminal_interface_base.py
M scripts/commons_information.py
M scripts/listpages.py
M tests/tools_formatter_tests.py
M tests/ui_tests.py
7 files changed, 72 insertions(+), 105 deletions(-)

Approvals:
  Xqt: Looks good to me, approved
  jenkins-bot: Verified



diff --git a/pywikibot/bot_choice.py b/pywikibot/bot_choice.py
index a1b3ac6..8e4e7b3 100644
--- a/pywikibot/bot_choice.py
+++ b/pywikibot/bot_choice.py
@@ -59,7 +59,7 @@
             formatted_options.append(option.format(default=default))
         # remove color highlights before fill function
         text = '{} ({})'.format(text, ', '.join(formatted_options))
-        pattern = '\03{[a-z]+}'
+        pattern = '<<[a-z]+>>'
         highlights = re.findall(pattern, text)
         return fill(re.sub(pattern, '{}', text), width=77).format(*highlights)

diff --git a/pywikibot/tools/formatter.py b/pywikibot/tools/formatter.py
index 6a4bf2f..80f207b 100644
--- a/pywikibot/tools/formatter.py
+++ b/pywikibot/tools/formatter.py
@@ -5,13 +5,11 @@
 # Distributed under the terms of the MIT license.
 #
 import math
-import platform
-from string import Formatter
-from typing import Any, Mapping, Sequence
+import re

 from pywikibot.logging import output
 from pywikibot.tools import deprecated
-from pywikibot.userinterfaces.terminal_interface_base import colors
+from pywikibot.userinterfaces import terminal_interface_base


 class SequenceOutputter:
@@ -65,64 +63,6 @@
         output(self.out)


-class _ColorFormatter(Formatter):
-
-    """Special string formatter which skips colors."""
-
-    colors = set(colors)
-    # Dot.product of colors to create all possible combinations of foreground
-    # and background colors.
-    colors |= {'{};{}'.format(c1, c2) for c1 in colors for c2 in colors}
-
-    def get_value(self, key, args, kwargs):
-        """Get value, filling in 'color' when it is a valid color."""
-        if key == 'color' and kwargs.get('color') in self.colors:
-            return '\03{{{}}}'.format(kwargs[key])
-        return super().get_value(key, args, kwargs)
-
-    def parse(self, format_string: str):
-        """Yield results similar to parse but skip colors."""
-        previous_literal = ''
-        for literal, field, spec, conv in super().parse(format_string):
-            if field in self.colors:
-                if spec:
-                    raise ValueError(
-                        'Color field "{}" in "{}" uses format spec '
-                        'information "{}"'.format(field, format_string, spec))
-                if conv:
-                    raise ValueError(
-                        'Color field "{}" in "{}" uses conversion '
-                        'information "{}"'.format(field, format_string, conv))
-                if not literal or literal[-1] != '\03':
-                    literal += '\03'
-                if '\03' in literal[:-1]:
-                    raise ValueError(r'Literal text in {} contains '
-                                     r'\03'.format(format_string))
-                previous_literal += literal + '{' + field + '}'
-            else:
-                if '\03' in literal:
-                    raise ValueError(r'Literal text in {} contains '
-                                     r'\03'.format(format_string))
-                yield previous_literal + literal, field, spec, conv
-                previous_literal = ''
-        if previous_literal:
-            yield previous_literal, None, None, None
-
-    def vformat(self, format_string: str, args: Sequence,
-                kwargs: Mapping[str, Any]) -> str:
-        """Return the format result but verify no colors are keywords.
-
-        :param format_string: The format template string
-        :param args: The positional field values
-        :param kwargs: The named field values
-        :return: The formatted string
-        """
-        if self.colors.intersection(kwargs):  # kwargs use colors
-            raise ValueError('Keyword argument(s) use valid color(s): '
-                             + '", "'.join(self.colors.intersection(kwargs)))
-        return super().vformat(format_string, args, kwargs)
-
-
 @deprecated('New color format pattern like <<color>>colored text<<default>>',
             since='7.2.0')
 def color_format(text: str, *args, **kwargs) -> str:
@@ -142,8 +82,29 @@

     :param text: The format template string
     :return: The formatted string
+    :raises ValueError: Wrong format string or wrong keywords
     """
-    if platform.python_implementation() == 'PyPy' \
-       and isinstance(text, bytes):  # T296830
-        raise TypeError("'text' parameter must be a str not bytes")
-    return _ColorFormatter().format(text, *args, **kwargs)
+    colors = set(terminal_interface_base.colors)
+    # Dot.product of colors to create all possible combinations of foreground
+    # and background colors.
+    colors |= {'{};{}'.format(c1, c2) for c1 in colors for c2 in colors}
+    col_pat = '|'.join(colors)
+    text = re.sub('(?:\03)?{{({})}}'.format(col_pat), r'<<\1>>', text)
+    replace_color = kwargs.get('color')
+    if replace_color in colors:
+        text = text.replace('{color}', '<<{}>>'.format(replace_color))
+    if '\03' in text:
+        raise ValueError('\\03 pattern found in color format')
+    intersect = colors.intersection(kwargs)  # kwargs use colors
+    if intersect:
+        raise ValueError('Keyword argument(s) use valid color(s): '
+                         + '", "'.join(intersect))
+    try:
+        text = text.format(*args, **kwargs)
+    except KeyError as e:
+        if str(e).strip("'") in colors:
+            raise ValueError(
+                'Color field "{}" in "{}" uses conversion information or '
+                'format spec'.format(e, text))
+        raise
+    return text
diff --git a/pywikibot/userinterfaces/terminal_interface_base.py 
b/pywikibot/userinterfaces/terminal_interface_base.py
index 0444206..c49b3d0 100644
--- a/pywikibot/userinterfaces/terminal_interface_base.py
+++ b/pywikibot/userinterfaces/terminal_interface_base.py
@@ -22,7 +22,7 @@
     StandardOption,
 )
 from pywikibot.logging import INFO, INPUT, STDOUT, VERBOSE, WARNING
-from pywikibot.tools import RLock
+from pywikibot.tools import issue_deprecation_warning, RLock
 from pywikibot.userinterfaces import transliteration
 from pywikibot.userinterfaces._interface_base import ABUIC

@@ -201,7 +201,14 @@
             raise ValueError('Old color format must not be mixed with new '
                              'color format. Found:\n'
                              + text.replace('\03', '\\03'))
-        text_parts = old_parts if len(old_parts) > 1 else new_parts
+        if len(old_parts) > 1:
+            issue_deprecation_warning(
+                'old color format variant like \03{color}',
+                'new color format like <<color>>',
+                since='7.3.0')
+            text_parts = old_parts
+        else:
+            text_parts = new_parts
         text_parts += ['default']
         # match.split() includes every regex group; for each matched color
         # fg_col:b_col, fg_col and bg_col are added to the resulting list.
@@ -311,9 +318,9 @@
                     # could consist of multiple letters.
                     # mark the transliterated letters in yellow.
                     transliteratedText = ''.join((transliteratedText,
-                                                  '\03{lightyellow}',
+                                                  '<<lightyellow>>',
                                                   transliterated,
-                                                  '\03{previous}'))
+                                                  '<<previous>>'))
                     # memorize if we replaced a single letter by multiple
                     # letters.
                     if transliterated:
diff --git a/scripts/commons_information.py b/scripts/commons_information.py
index a17f134..5402092 100755
--- a/scripts/commons_information.py
+++ b/scripts/commons_information.py
@@ -69,10 +69,10 @@
                 tmp_page2 = pywikibot.Page(self.site, langs[0].lang, ns=10)
                 if tmp_page2 != tmp_page:
                     pywikibot.output(
-                        '\03{{lightblue}}The language template {before!r} '
+                        '<<lightblue>>The language template {before!r} '
                         'was found, but langdetect thinks {after!r} is the '
                         'most appropriate with a probability of {prob}:'
-                        '\03{{default}}\n{text}'
+                        '<<default>>\n{text}'
                         .format(before=tmp_page.title(with_ns=False),
                                 after=tmp_page2.title(with_ns=False),
                                 prob=langs[0].prob,
diff --git a/scripts/listpages.py b/scripts/listpages.py
index 0c5e9b2..f206e25 100755
--- a/scripts/listpages.py
+++ b/scripts/listpages.py
@@ -24,7 +24,7 @@
             4 - '[[{page.title}]]'
                 --> [[PageTitle]]

-            5 - '{num:4d} \03{{lightred}}{page.loc_title:<40}\03{{default}}'
+            5 - '{num:4d} <<lightred>>{page.loc_title:<40}<<default>>'
                 --> 10 localised_Namespace:PageTitle (colorised in lightred)

             6 - '{num:4d} {page.loc_title:<40} {page.can_title:<40}'
@@ -96,8 +96,9 @@

 import pywikibot
 from pywikibot import config, i18n
-from pywikibot.exceptions import Error
+from pywikibot.exceptions import ArgumentDeprecationWarning, Error
 from pywikibot.pagegenerators import GeneratorFactory, parameterHelp
+from pywikibot.tools import issue_deprecation_warning


 docuReplacements = {'&params;': parameterHelp}  # noqa: N816
@@ -112,7 +113,7 @@
         '2': '{num:4d} [[{page.title}]]',
         '3': '{page.title}',
         '4': '[[{page.title}]]',
-        '5': '{num:4d} \03{{lightred}}{page.loc_title:<40}\03{{default}}',
+        '5': '{num:4d} <<lightred>>{page.loc_title:<40}<<default>>',
         '6': '{num:4d} {page.loc_title:<40} {page.can_title:<40}',
         '7': '{num:4d} {page.loc_title:<40} {page.trs_title:<40}',
     }
@@ -193,7 +194,13 @@
         if option == '-notitle':
             notitle = True
         elif option == '-format':
-            fmt = value.replace('\\03{{', '\03{{')
+            if '\\03{{' in value:
+                fmt = value.replace('\\03{{', '\03{{')
+                issue_deprecation_warning(
+                    'old color format variant like \03{color}',
+                    'new color format like <<color>>',
+                    warning_class=ArgumentDeprecationWarning,
+                    since='7.3.0')
             if not fmt.strip():
                 notitle = True
         elif option == '-outputlang':
diff --git a/tests/tools_formatter_tests.py b/tests/tools_formatter_tests.py
index 82c0a57..5345d80 100755
--- a/tests/tools_formatter_tests.py
+++ b/tests/tools_formatter_tests.py
@@ -60,29 +60,21 @@

     def test_colors(self):
         """Test with colors in template string."""
-        self.assert_format('{0}{black}', '42\03{black}', 42)
-        self.assert_format('{ans}{black}', '42\03{black}', ans=42)
-        with self.assertRaisesRegex(
-                ValueError,
-                r'.*conversion.*'):
+        self.assert_format('{0}{black}', '42<<black>>', 42)
+        self.assert_format('{ans}{black}', '42<<black>>', ans=42)
+        with self.assertRaisesRegex(ValueError, r'.*conversion.*'):
             formatter.color_format('{0}{black!r}', 42)
-        with self.assertRaisesRegex(
-                ValueError,
-                r'.*format spec.*'):
+        with self.assertRaisesRegex(ValueError, r'.*format spec.*'):
             formatter.color_format('{0}{black:03}', 42)

     def test_marker(self):
         r"""Test that the \03 marker is only allowed in front of colors."""
-        self.assert_format('{0}\03{black}', '42\03{black}', 42)
+        self.assert_format('{0}\03{black}', '42<<black>>', 42)
         # literal before a normal field
-        with self.assertRaisesRegex(
-                ValueError,
-                r'.*\\03'):
+        with self.assertRaisesRegex(ValueError, r'.*\\03'):
             formatter.color_format('\03{0}{black}', 42)
         # literal before a color field
-        with self.assertRaisesRegex(
-                ValueError,
-                r'.*\\03'):
+        with self.assertRaisesRegex(ValueError, r'.*\\03'):
             formatter.color_format('{0}\03before{black}', 42)

     def test_color_kwargs(self):
@@ -93,9 +85,9 @@
     def test_non_ascii(self):
         """Test non-ASCII replacements."""
         self.assert_format('{0}', 'ä', 'ä')
-        self.assert_format('{black}{0}', '\03{black}ä', 'ä')
+        self.assert_format('{black}{0}', '<<black>>ä', 'ä')
         self.assert_format('{0}', 'ä', self.DummyUnicode())
-        self.assert_format('{black}{0}', '\03{black}ä', self.DummyUnicode())
+        self.assert_format('{black}{0}', '<<black>>ä', self.DummyUnicode())

     def test_bytes_format(self):
         """Test that using `bytes` is not allowed."""
@@ -106,8 +98,8 @@

     def test_variant_colors(self):
         """Test variant colors with {color} parameter."""
-        self.assert_format('{0}{color}', '42\03{black}', 42, color='black')
-        self.assert_format('{ans}{color}', '42\03{black}', ans=42,
+        self.assert_format('{0}{color}', '42<<black>>', 42, color='black')
+        self.assert_format('{ans}{color}', '42<<black>>', ans=42,
                            color='black')
         self.assert_format('{color}', '42', color=42)

diff --git a/tests/ui_tests.py b/tests/ui_tests.py
index 3c81000..57654eb 100755
--- a/tests/ui_tests.py
+++ b/tests/ui_tests.py
@@ -281,7 +281,7 @@

     """Terminal output color tests."""

-    str1 = 'text \03{lightpurple}light purple text\03{default} text'
+    str1 = 'text <<lightpurple>>light purple text<<default>> text'

     def testOutputColorizedText(self):
         pywikibot.output(self.str1)
@@ -298,9 +298,9 @@
             self.strerr.getvalue(),
             'text light purple text text ***\n')

-    str2 = ('normal text \03{lightpurple} light purple '
-            '\03{lightblue} light blue \03{previous} light purple '
-            '\03{default} normal text')
+    str2 = ('normal text <<lightpurple>> light purple '
+            '<<lightblue>> light blue <<previous>> light purple '
+            '<<default>> normal text')

     def testOutputColorCascade_incorrect(self):
         """Test incorrect behavior of testOutputColorCascade."""
@@ -420,7 +420,7 @@
         """Test a string using one color."""
         self._colors = (('red', 6), ('default', 10))
         with redirect_stdout(self.redirect) as f:
-            self.ui_obj._print('Hello \03{red}world you!', self.ui_obj.stdout)
+            self.ui_obj._print('Hello <<red>>world you!', self.ui_obj.stdout)
         self.assertEqual(f.getvalue(), self.expected)

     def test_flat_color(self):
@@ -429,7 +429,7 @@
                         ('default', 1))
         with redirect_stdout(self.redirect) as f:
             self.ui_obj._print(
-                'Hello \03{red}world \03{default}you\03{yellow}!',
+                'Hello <<red>>world <<default>>you<<yellow>>!',
                 self.ui_obj.stdout)
         self.assertEqual(f.getvalue(), self.expected)

@@ -438,7 +438,7 @@
         self._colors = (('red', 6), ('yellow', 6), ('red', 3), ('default', 1))
         with redirect_stdout(self.redirect) as f:
             self.ui_obj._print(
-                'Hello \03{red}world \03{yellow}you\03{previous}!',
+                'Hello <<red>>world <<yellow>>you<<previous>>!',
                 self.ui_obj.stdout)
         self.assertEqual(f.getvalue(), self.expected)

@@ -446,7 +446,7 @@
         """Test using stacked colors without popping any."""
         self._colors = (('red', 6), ('yellow', 6), ('default', 4))
         with redirect_stdout(self.redirect) as f:
-            self.ui_obj._print('Hello \03{red}world \03{yellow}you!',
+            self.ui_obj._print('Hello <<red>>world <<yellow>>you!',
                                self.ui_obj.stdout)
         self.assertEqual(f.getvalue(), self.expected)

@@ -454,7 +454,7 @@
         """Test with trailing new line and one color."""
         self._colors = (('red', 6), ('default', 11))
         with redirect_stdout(self.redirect) as f:
-            self.ui_obj._print('Hello \03{red}world you!\n',
+            self.ui_obj._print('Hello <<red>>world you!\n',
                                self.ui_obj.stdout)
         self.assertEqual(f.getvalue(), self.expected + '\n')


--
To view, visit https://gerrit.wikimedia.org/r/c/pywikibot/core/+/789191
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.wikimedia.org/r/settings

Gerrit-Project: pywikibot/core
Gerrit-Branch: master
Gerrit-Change-Id: I811d85ab7205f43da798d42c72ff81283a5ccecd
Gerrit-Change-Number: 789191
Gerrit-PatchSet: 4
Gerrit-Owner: Xqt <[email protected]>
Gerrit-Reviewer: JAn Dudík <[email protected]>
Gerrit-Reviewer: Xqt <[email protected]>
Gerrit-Reviewer: jenkins-bot
Gerrit-MessageType: merged
_______________________________________________
Pywikibot-commits mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to