Skip to content

Commit

Permalink
cleanup: remove old color format escape sequences usage
Browse files Browse the repository at this point in the history
Bug: T378898
Change-Id: I82d50efe9ec39e03c6bc7be55b1c1e4ba787dc82
  • Loading branch information
xqt committed Dec 4, 2024
1 parent 2b7d646 commit 1032e6e
Show file tree
Hide file tree
Showing 6 changed files with 15 additions and 162 deletions.
6 changes: 3 additions & 3 deletions ROADMAP.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ Current Release Changes

**Breaking changes and code cleanups**

* 7.2.0: RedirectPageBot and NoRedirectPageBot bot classes were removed in favour of
* Old color escape sequences like ``\03{color}`` were dropped in favour of new color format like ``<<color>>``
* ``tools.formatter.color_format()`` was removed; the new color literals can be used instead
* RedirectPageBot and NoRedirectPageBot bot classes were removed in favour of
:attr:`use_redirects<bot.BaseBot.use_redirects>` attribute
* Python 3.7 support was dropped (:phab:`T378893`)

Expand Down Expand Up @@ -89,15 +91,13 @@ Pending removal in Pywikibot 10
* 7.6.0: :mod:`tools.collections` datatypes should no longer imported from :mod:`tools`
* 7.5.0: :mod:`textlib`.tzoneFixedOffset class will be removed in favour of :class:`time.TZoneFixedOffset`
* 7.4.0: ``FilePage.usingPages()`` was renamed to :meth:`using_pages()<pywikibot.FilePage.using_pages>`
* 7.3.0: Old color escape sequences like ``\03{color}`` is deprecated in favour of new color format like <<color>>
* 7.3.0: ``linktrail`` method of :class:`family.Family` is deprecated; use :meth:`APISite.linktrail()
<pywikibot.site._apisite.APISite.linktrail>` instead
* 7.2.0: Positional arguments *decoder*, *layer* and *newline* for :mod:`logging` functions were dropped; keyword
arguments must be used instead.
* 7.2.0: ``tb`` parameter of :func:`exception()<pywikibot.logging.exception>` function was renamed to ``exc_info``
* 7.2.0: XMLDumpOldPageGenerator is deprecated in favour of a ``content`` parameter of
:func:`XMLDumpPageGenerator<pagegenerators.XMLDumpPageGenerator>` (:phab:`T306134`)
* 7.2.0: :func:`tools.formatter.color_format<tools.formatter.color_format>` is deprecated and will be removed
* 7.1.0: Unused ``get_redirect`` parameter of :meth:`Page.getOldVersion()<page.BasePage.getOldVersion>` will be removed
* 7.0.0: User.isBlocked() method is renamed to is_blocked for consistency
* 7.0.0: A boolean watch parameter in Page.save() is deprecated and will be desupported
Expand Down
6 changes: 3 additions & 3 deletions pywikibot/tools/_logging.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
"""Logging tools."""
#
# (C) Pywikibot team, 2009-2022
# (C) Pywikibot team, 2009-2024
#
# Distributed under the terms of the MIT license.
#
from __future__ import annotations

import logging

from pywikibot.userinterfaces.terminal_interface_base import new_colorTagR
from pywikibot.userinterfaces.terminal_interface_base import colorTagR


class LoggingFormatter(logging.Formatter):
Expand All @@ -33,6 +33,6 @@ def format(self, record):

# remove color tags
if record.msg and isinstance(record.msg, str):
record.msg = new_colorTagR.sub('', record.msg)
record.msg = colorTagR.sub('', record.msg)

return super().format(record).rstrip()
49 changes: 1 addition & 48 deletions pywikibot/tools/formatter.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
"""Module containing various formatting related utilities."""
#
# (C) Pywikibot team, 2015-2023
# (C) Pywikibot team, 2015-2024
#
# Distributed under the terms of the MIT license.
#
from __future__ import annotations

import math
import re

from pywikibot.logging import info
from pywikibot.tools import deprecated
from pywikibot.userinterfaces import terminal_interface_base


class SequenceOutputter:
Expand Down Expand Up @@ -64,48 +62,3 @@ def output(self) -> None:
:attr:`out` property.
"""
info(self.out)


@deprecated('New color format pattern like <<color>>colored text<<default>>',
since='7.2.0')
def color_format(text: str, *args, **kwargs) -> str:
r"""Do ``str.format`` without having to worry about colors.
It is automatically adding \\03 in front of color fields so it's
unnecessary to add them manually. Any other \\03 in the text is
disallowed.
You may use a variant {color} by assigning a valid color to a named
parameter color.
.. deprecated:: 7.2
new color format pattern like
``f'<<{color}>>colored text<<default>>'`` can be used instead.
:param text: The format template string
:return: The formatted string
:raises ValueError: Wrong format string or wrong keywords
"""
colors = set(terminal_interface_base.colors)
# Dot.product of colors to create all possible combinations of foreground
# and background colors.
colors |= {f'{c1};{c2}' for c1 in colors for c2 in colors}
col_pat = '|'.join(colors)
text = re.sub(f'(?:\03)?{{({col_pat})}}', r'<<\1>>', text)
replace_color = kwargs.get('color')
if replace_color in colors:
text = text.replace('{color}', f'<<{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(f'Color field "{e}" in "{text}" uses conversion '
f'information or format spec')
raise
return text
21 changes: 3 additions & 18 deletions pywikibot/userinterfaces/terminal_interface_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
StandardOption,
)
from pywikibot.logging import INFO, INPUT, STDOUT, VERBOSE, WARNING
from pywikibot.tools import issue_deprecation_warning
from pywikibot.tools.threading import RLock
from pywikibot.userinterfaces import transliteration
from pywikibot.userinterfaces._interface_base import ABUIC
Expand Down Expand Up @@ -53,9 +52,8 @@
'white',
]

_color_pat = '((:?{0});?(:?{0})?)'.format('|'.join([*colors, 'previous']))
old_colorTagR = re.compile(f'\03{{{_color_pat}}}')
new_colorTagR = re.compile(f'<<{_color_pat}>>')
colorTagR = re.compile(
'<<((:?{0});?(:?{0})?)>>'.format('|'.join([*colors, 'previous'])))


class UI(ABUIC):
Expand Down Expand Up @@ -195,20 +193,7 @@ def _print(self, text, target_stream) -> None:
# Color tags might be cascaded, e.g. because of transliteration.
# Therefore we need this stack.
color_stack = ['default']
old_parts = old_colorTagR.split(text)
new_parts = new_colorTagR.split(text)
if min(len(old_parts), len(new_parts)) > 1:
raise ValueError('Old color format must not be mixed with new '
'color format. Found:\n'
+ text.replace('\03', '\\03'))
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 = colorTagR.split(text)
text_parts.append('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.
Expand Down
16 changes: 3 additions & 13 deletions scripts/listpages.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,8 @@
import pywikibot
from pywikibot import config
from pywikibot.bot import AutomaticTWSummaryBot, SingleSiteBot, suggest_help
from pywikibot.exceptions import ArgumentDeprecationWarning, Error
from pywikibot.exceptions import Error
from pywikibot.pagegenerators import GeneratorFactory, parameterHelp
from pywikibot.tools import issue_deprecation_warning


docuReplacements = {'&params;': parameterHelp} # noqa: N816
Expand Down Expand Up @@ -293,18 +292,9 @@ def main(*args: str) -> None:
if option in ('-get', '-notitle', '-overwrite'):
options[opt] = True
elif option == '-format':
if '\\03{{' not in value:
fmt = value
else:
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():
if not value.strip():
options['notitle'] = True
options['format'] = fmt
options['format'] = value
elif option in ('-encode', '-outputlang', '-save', '-summary'):
options[opt] = value
elif option == '-put':
Expand Down
79 changes: 2 additions & 77 deletions tests/tools_formatter_tests.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
#!/usr/bin/env python3
"""Tests for the ``pywikibot.tools.formatter`` module."""
#
# (C) Pywikibot team, 2015-2022
# (C) Pywikibot team, 2015-2024
#
# Distributed under the terms of the MIT license.
#
Expand All @@ -11,7 +11,7 @@
from contextlib import suppress

from pywikibot.tools import formatter
from tests.aspects import DeprecationTestCase, TestCase
from tests.aspects import TestCase


class TestListOutputter(TestCase):
Expand All @@ -31,81 +31,6 @@ def test_SequenceOutputter(self):
self.assertEqual(outputter.out, '\nfoo\nbar\n')


class TestColorFormat(DeprecationTestCase):

"""Test color_format function in bot module."""

class DummyUnicode:

"""Dummy class that __str__ returns a non-ASCII Unicode value."""

def __str__(self):
"""Return ä."""
return 'ä'

net = False

def assert_format(self, format_string, expected, *args, **kwargs):
"""Assert that color_format returns the expected string and type."""
result = formatter.color_format(format_string, *args, **kwargs)
self.assertEqual(result, expected)
self.assertIsInstance(result, type(expected))
self.assertOneDeprecation()

def test_no_colors(self):
"""Test without colors in template string."""
self.assert_format('', '')
self.assert_format('42', '42')
self.assert_format('{0}', '42', 42)
self.assert_format('before {0} after', 'before 42 after', 42)
self.assert_format('{ans}', '42', ans=42)

def test_colors(self):
"""Test with colors in template string."""
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.*'):
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<<black>>', 42)
# literal before a normal field
with self.assertRaisesRegex(ValueError, r'.*\\03'):
formatter.color_format('\03{0}{black}', 42)
# literal before a color field
with self.assertRaisesRegex(ValueError, r'.*\\03'):
formatter.color_format('{0}\03before{black}', 42)

def test_color_kwargs(self):
"""Test with a color as keyword argument."""
with self.assertRaises(ValueError):
formatter.color_format('{aqua}{black}', aqua=42)

def test_non_ascii(self):
"""Test non-ASCII replacements."""
self.assert_format('{0}', 'ä', 'ä')
self.assert_format('{black}{0}', '<<black>>ä', 'ä')
self.assert_format('{0}', 'ä', self.DummyUnicode())
self.assert_format('{black}{0}', '<<black>>ä', self.DummyUnicode())

def test_bytes_format(self):
"""Test that using `bytes` is not allowed."""
with self.assertRaises(TypeError):
formatter.color_format(b'{0}', 'a')
with self.assertRaises(TypeError):
formatter.color_format(b'{black}{0}', 'a')

def test_variant_colors(self):
"""Test variant colors with {color} parameter."""
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)


if __name__ == '__main__':
with suppress(SystemExit):
unittest.main()

0 comments on commit 1032e6e

Please sign in to comment.