Skip to content

Commit

Permalink
Refactored color_style() and no_style() to improve testability. Refs …
Browse files Browse the repository at this point in the history
…#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__.
  • Loading branch information
loic committed Oct 22, 2014
1 parent bdb4118 commit eb82fb0
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 29 deletions.
74 changes: 45 additions & 29 deletions django/core/management/color.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,52 +5,68 @@
import os
import sys

from django.utils import lru_cache
from django.utils import termcolors


def supports_color():
"""
Returns True if the running system's terminal supports color, and False
otherwise.
Returns True if the running system's terminal supports color,
and False otherwise.
"""
plat = sys.platform
supported_platform = plat != 'Pocket PC' and (plat != 'win32' or
'ANSICON' in os.environ)
supported_platform = plat != 'Pocket PC' and (plat != 'win32' or 'ANSICON' in os.environ)

# isatty is not always implemented, #6223.
is_a_tty = hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()
if not supported_platform or not is_a_tty:
return False
return True


def color_style():
"""Returns a Style object with the Django color scheme."""
if not supports_color():
style = no_style()
else:
DJANGO_COLORS = os.environ.get('DJANGO_COLORS', '')
color_settings = termcolors.parse_color_setting(DJANGO_COLORS)
def make_style(config_string=''):
"""
Create a Style object from the given config_string.
If config_string is empty django.utils.termcolors.DEFAULT_PALETTE is used.
"""
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:
class dummy:
pass
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
format = color_settings.get(role, {})
style_func = termcolors.make_style(**format)
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


@lru_cache.lru_cache(maxsize=None)
def no_style():
"""Returns a Style object that has no colors."""
class dummy:
def __getattr__(self, attr):
return lambda x: x
return dummy()
"""
Returns a Style object with no color scheme.
"""
return make_style('nocolor')


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', ''))
16 changes: 16 additions & 0 deletions tests/admin_scripts/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -1392,6 +1392,22 @@ def test_specific_help(self):
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)

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):
class Command(BaseCommand):
requires_system_checks = False
Expand Down

0 comments on commit eb82fb0

Please sign in to comment.