Refactored color_style() and no_style() to improve testability. Refs #23663.
This includes the following improvements: - The type of the style object is now called 'Style' rather than 'dummy'. - The new make_style() function allows generating a Style object directly from a config string. Before the only way to get a style object was through the environ and it also required that the terminal supported colors which isn't necessarily the case when testing. - The output of no_style() is now cached with @lru_cache. - The output of no_style() now has the same set of attributes as the other Style objects. Previously it allowed anything to pass through with __getattr__.
This commit is contained in:
parent
bdb4118b1a
commit
eb82fb0a9d
|
@ -5,17 +5,18 @@ Sets up the terminal color scheme.
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
from django.utils import lru_cache
|
||||||
from django.utils import termcolors
|
from django.utils import termcolors
|
||||||
|
|
||||||
|
|
||||||
def supports_color():
|
def supports_color():
|
||||||
"""
|
"""
|
||||||
Returns True if the running system's terminal supports color, and False
|
Returns True if the running system's terminal supports color,
|
||||||
otherwise.
|
and False otherwise.
|
||||||
"""
|
"""
|
||||||
plat = sys.platform
|
plat = sys.platform
|
||||||
supported_platform = plat != 'Pocket PC' and (plat != 'win32' or
|
supported_platform = plat != 'Pocket PC' and (plat != 'win32' or 'ANSICON' in os.environ)
|
||||||
'ANSICON' in os.environ)
|
|
||||||
# isatty is not always implemented, #6223.
|
# isatty is not always implemented, #6223.
|
||||||
is_a_tty = hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()
|
is_a_tty = hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()
|
||||||
if not supported_platform or not is_a_tty:
|
if not supported_platform or not is_a_tty:
|
||||||
|
@ -23,34 +24,49 @@ def supports_color():
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def color_style():
|
def make_style(config_string=''):
|
||||||
"""Returns a Style object with the Django color scheme."""
|
"""
|
||||||
if not supports_color():
|
Create a Style object from the given config_string.
|
||||||
style = no_style()
|
|
||||||
else:
|
If config_string is empty django.utils.termcolors.DEFAULT_PALETTE is used.
|
||||||
DJANGO_COLORS = os.environ.get('DJANGO_COLORS', '')
|
"""
|
||||||
color_settings = termcolors.parse_color_setting(DJANGO_COLORS)
|
class Style(object):
|
||||||
|
pass
|
||||||
|
|
||||||
|
style = Style()
|
||||||
|
|
||||||
|
color_settings = termcolors.parse_color_setting(config_string)
|
||||||
|
|
||||||
|
# The nocolor palette has all available roles.
|
||||||
|
# Use that palette as the basis for populating
|
||||||
|
# the palette as defined in the environment.
|
||||||
|
for role in termcolors.PALETTES[termcolors.NOCOLOR_PALETTE]:
|
||||||
if color_settings:
|
if color_settings:
|
||||||
class dummy:
|
format = color_settings.get(role, {})
|
||||||
pass
|
style_func = termcolors.make_style(**format)
|
||||||
style = dummy()
|
|
||||||
# The nocolor palette has all available roles.
|
|
||||||
# Use that palette as the basis for populating
|
|
||||||
# the palette as defined in the environment.
|
|
||||||
for role in termcolors.PALETTES[termcolors.NOCOLOR_PALETTE]:
|
|
||||||
format = color_settings.get(role, {})
|
|
||||||
setattr(style, role, termcolors.make_style(**format))
|
|
||||||
# For backwards compatibility,
|
|
||||||
# set style for ERROR_OUTPUT == ERROR
|
|
||||||
style.ERROR_OUTPUT = style.ERROR
|
|
||||||
else:
|
else:
|
||||||
style = no_style()
|
style_func = lambda x: x
|
||||||
|
setattr(style, role, style_func)
|
||||||
|
|
||||||
|
# For backwards compatibility,
|
||||||
|
# set style for ERROR_OUTPUT == ERROR
|
||||||
|
style.ERROR_OUTPUT = style.ERROR
|
||||||
|
|
||||||
return style
|
return style
|
||||||
|
|
||||||
|
|
||||||
|
@lru_cache.lru_cache(maxsize=None)
|
||||||
def no_style():
|
def no_style():
|
||||||
"""Returns a Style object that has no colors."""
|
"""
|
||||||
class dummy:
|
Returns a Style object with no color scheme.
|
||||||
def __getattr__(self, attr):
|
"""
|
||||||
return lambda x: x
|
return make_style('nocolor')
|
||||||
return dummy()
|
|
||||||
|
|
||||||
|
def color_style():
|
||||||
|
"""
|
||||||
|
Returns a Style object from the Django color scheme.
|
||||||
|
"""
|
||||||
|
if not supports_color():
|
||||||
|
return no_style()
|
||||||
|
return make_style(os.environ.get('DJANGO_COLORS', ''))
|
||||||
|
|
|
@ -1392,6 +1392,22 @@ class CommandTypes(AdminScriptTestCase):
|
||||||
self.assertOutput(out, "Prints the CREATE TABLE, custom SQL and CREATE INDEX SQL statements for the\ngiven model module name(s).")
|
self.assertOutput(out, "Prints the CREATE TABLE, custom SQL and CREATE INDEX SQL statements for the\ngiven model module name(s).")
|
||||||
self.assertEqual(out.count('optional arguments'), 1)
|
self.assertEqual(out.count('optional arguments'), 1)
|
||||||
|
|
||||||
|
def test_color_style(self):
|
||||||
|
style = color.no_style()
|
||||||
|
self.assertEqual(style.ERROR('Hello, world!'), 'Hello, world!')
|
||||||
|
|
||||||
|
style = color.make_style('nocolor')
|
||||||
|
self.assertEqual(style.ERROR('Hello, world!'), 'Hello, world!')
|
||||||
|
|
||||||
|
style = color.make_style('dark')
|
||||||
|
self.assertIn('Hello, world!', style.ERROR('Hello, world!'))
|
||||||
|
self.assertNotEqual(style.ERROR('Hello, world!'), 'Hello, world!')
|
||||||
|
|
||||||
|
# Default palette has color.
|
||||||
|
style = color.make_style('')
|
||||||
|
self.assertIn('Hello, world!', style.ERROR('Hello, world!'))
|
||||||
|
self.assertNotEqual(style.ERROR('Hello, world!'), 'Hello, world!')
|
||||||
|
|
||||||
def test_command_color(self):
|
def test_command_color(self):
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
requires_system_checks = False
|
requires_system_checks = False
|
||||||
|
|
Loading…
Reference in New Issue