mirror of
https://github.com/django/django.git
synced 2024-12-01 15:42:04 +01:00
Fixed #12112 -- Made the colors used by syntax highlighting customizable.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@12009 bcc190cf-cafb-0310-a4f2-bffc1f526a37
This commit is contained in:
parent
9319f89547
commit
c38d66a216
@ -2,6 +2,7 @@
|
||||
Sets up the terminal color scheme.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
from django.utils import termcolors
|
||||
@ -21,16 +22,21 @@ def supports_color():
|
||||
def color_style():
|
||||
"""Returns a Style object with the Django color scheme."""
|
||||
if not supports_color():
|
||||
return no_style()
|
||||
class dummy: pass
|
||||
style = dummy()
|
||||
style.ERROR = termcolors.make_style(fg='red', opts=('bold',))
|
||||
style.ERROR_OUTPUT = termcolors.make_style(fg='red', opts=('bold',))
|
||||
style.NOTICE = termcolors.make_style(fg='red')
|
||||
style.SQL_FIELD = termcolors.make_style(fg='green', opts=('bold',))
|
||||
style.SQL_COLTYPE = termcolors.make_style(fg='green')
|
||||
style.SQL_KEYWORD = termcolors.make_style(fg='yellow')
|
||||
style.SQL_TABLE = termcolors.make_style(opts=('bold',))
|
||||
style = no_style()
|
||||
else:
|
||||
DJANGO_COLORS = os.environ.get('DJANGO_COLORS', '')
|
||||
color_settings = termcolors.parse_color_setting(DJANGO_COLORS)
|
||||
if color_settings:
|
||||
class dummy: pass
|
||||
style = dummy()
|
||||
# The nocolor palette has all available roles.
|
||||
# Use that pallete 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))
|
||||
else:
|
||||
style = no_style()
|
||||
return style
|
||||
|
||||
def no_style():
|
||||
|
@ -5,7 +5,6 @@ termcolors.py
|
||||
color_names = ('black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white')
|
||||
foreground = dict([(color_names[x], '3%s' % x) for x in range(8)])
|
||||
background = dict([(color_names[x], '4%s' % x) for x in range(8)])
|
||||
del color_names
|
||||
|
||||
RESET = '0'
|
||||
opt_dict = {'bold': '1', 'underscore': '4', 'blink': '5', 'reverse': '7', 'conceal': '8'}
|
||||
@ -66,3 +65,112 @@ def make_style(opts=(), **kwargs):
|
||||
COMMENT = make_style(fg='blue', opts=('bold',))
|
||||
"""
|
||||
return lambda text: colorize(text, opts, **kwargs)
|
||||
|
||||
NOCOLOR_PALETTE = 'nocolor'
|
||||
DARK_PALETTE = 'dark'
|
||||
LIGHT_PALETTE = 'light'
|
||||
|
||||
PALETTES = {
|
||||
NOCOLOR_PALETTE: {
|
||||
'ERROR': {},
|
||||
'NOTICE': {},
|
||||
'SQL_FIELD': {},
|
||||
'SQL_COLTYPE': {},
|
||||
'SQL_KEYWORD': {},
|
||||
'SQL_TABLE': {},
|
||||
},
|
||||
DARK_PALETTE: {
|
||||
'ERROR': { 'fg': 'red', 'opts': ('bold',) },
|
||||
'NOTICE': { 'fg': 'red' },
|
||||
'SQL_FIELD': { 'fg': 'green', 'opts': ('bold',) },
|
||||
'SQL_COLTYPE': { 'fg': 'green' },
|
||||
'SQL_KEYWORD': { 'fg': 'yellow' },
|
||||
'SQL_TABLE': { 'opts': ('bold',) },
|
||||
},
|
||||
LIGHT_PALETTE: {
|
||||
'ERROR': { 'fg': 'red', 'opts': ('bold',) },
|
||||
'NOTICE': { 'fg': 'red' },
|
||||
'SQL_FIELD': { 'fg': 'green', 'opts': ('bold',) },
|
||||
'SQL_COLTYPE': { 'fg': 'green' },
|
||||
'SQL_KEYWORD': { 'fg': 'blue' },
|
||||
'SQL_TABLE': { 'opts': ('bold',) },
|
||||
}
|
||||
}
|
||||
DEFAULT_PALETTE = DARK_PALETTE
|
||||
|
||||
def parse_color_setting(config_string):
|
||||
"""Parse a DJANGO_COLORS environment variable to produce the system palette
|
||||
|
||||
The general form of a pallete definition is:
|
||||
|
||||
"palette;role=fg;role=fg/bg;role=fg,option,option;role=fg/bg,option,option"
|
||||
|
||||
where:
|
||||
palette is a named palette; one of 'light', 'dark', or 'nocolor'.
|
||||
role is a named style used by Django
|
||||
fg is a background color.
|
||||
bg is a background color.
|
||||
option is a display options.
|
||||
|
||||
Specifying a named palette is the same as manually specifying the individual
|
||||
definitions for each role. Any individual definitions following the pallete
|
||||
definition will augment the base palette definition.
|
||||
|
||||
Valid roles:
|
||||
'error', 'notice', 'sql_field', 'sql_coltype', 'sql_keyword', 'sql_table'
|
||||
|
||||
Valid colors:
|
||||
'black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'
|
||||
|
||||
Valid options:
|
||||
'bold', 'underscore', 'blink', 'reverse', 'conceal'
|
||||
|
||||
"""
|
||||
if not config_string:
|
||||
return PALETTES[DEFAULT_PALETTE]
|
||||
|
||||
# Split the color configuration into parts
|
||||
parts = config_string.lower().split(';')
|
||||
palette = PALETTES[NOCOLOR_PALETTE].copy()
|
||||
for part in parts:
|
||||
if part in PALETTES:
|
||||
# A default palette has been specified
|
||||
palette.update(PALETTES[part])
|
||||
elif '=' in part:
|
||||
# Process a palette defining string
|
||||
definition = {}
|
||||
|
||||
# Break the definition into the role,
|
||||
# plus the list of specific instructions.
|
||||
# The role must be in upper case
|
||||
role, instructions = part.split('=')
|
||||
role = role.upper()
|
||||
|
||||
styles = instructions.split(',')
|
||||
styles.reverse()
|
||||
|
||||
# The first instruction can contain a slash
|
||||
# to break apart fg/bg.
|
||||
colors = styles.pop().split('/')
|
||||
colors.reverse()
|
||||
fg = colors.pop()
|
||||
if fg in color_names:
|
||||
definition['fg'] = fg
|
||||
if colors and colors[-1] in color_names:
|
||||
definition['bg'] = colors[-1]
|
||||
|
||||
# All remaining instructions are options
|
||||
opts = tuple(s for s in styles if s in opt_dict.keys())
|
||||
if opts:
|
||||
definition['opts'] = opts
|
||||
|
||||
# The nocolor palette has all available roles.
|
||||
# Use that palette as the basis for determining
|
||||
# if the role is valid.
|
||||
if role in PALETTES[NOCOLOR_PALETTE] and definition:
|
||||
palette[role] = definition
|
||||
|
||||
# If there are no colors specified, return the empty palette.
|
||||
if palette == PALETTES[NOCOLOR_PALETTE]:
|
||||
return None
|
||||
return palette
|
||||
|
@ -991,13 +991,92 @@ being executed as an unattended, automated script.
|
||||
Extra niceties
|
||||
==============
|
||||
|
||||
.. _syntax-coloring:
|
||||
|
||||
Syntax coloring
|
||||
---------------
|
||||
|
||||
The ``django-admin.py`` / ``manage.py`` commands that output SQL to standard
|
||||
output will use pretty color-coded output if your terminal supports
|
||||
ANSI-colored output. It won't use the color codes if you're piping the
|
||||
command's output to another program.
|
||||
The ``django-admin.py`` / ``manage.py`` commands that output SQL to
|
||||
standard output will use pretty color-coded output if your terminal
|
||||
supports ANSI-colored output. It won't use the color codes if you're
|
||||
piping the command's output to another program.
|
||||
|
||||
The colors used for syntax highlighting can be customized. Django
|
||||
ships with three color palettes:
|
||||
|
||||
* ``dark``, suited to terminals that show white text on a black
|
||||
background. This is the default palette.
|
||||
|
||||
* ``light``, suited to terminals that show white text on a black
|
||||
background.
|
||||
|
||||
* ``nocolor``, which disables syntax highlighting.
|
||||
|
||||
You select a palette by setting a ``DJANGO_COLORS`` environment
|
||||
variable to specify the palette you want to use. For example, to
|
||||
specify the ``light`` palette under a Unix or OS/X BASH shell, you
|
||||
would run the following at a command prompt::
|
||||
|
||||
export DJANGO_COLORS="light"
|
||||
|
||||
You can also customize the colors that are used. Django specifies a
|
||||
number of roles in which color is used:
|
||||
|
||||
* ``error`` - A major error.
|
||||
* ``notice`` - A minor error.
|
||||
* ``sql_field`` - The name of a model field in SQL.
|
||||
* ``sql_coltype`` - The type of a model field in SQL.
|
||||
* ``sql_keyword`` - A SQL keyword.
|
||||
* ``sql_table`` - The name of a model in SQL.
|
||||
|
||||
Each of these roles can be assigned a specific foreground and
|
||||
background color, from the following list:
|
||||
|
||||
* ``black``
|
||||
* ``red``
|
||||
* ``green``
|
||||
* ``yellow``
|
||||
* ``blue``
|
||||
* ``magenta``
|
||||
* ``cyan``
|
||||
* ``white``
|
||||
|
||||
Each of these colors can then be modified by using the following
|
||||
display options:
|
||||
|
||||
* ``bold``
|
||||
* ``underscore``
|
||||
* ``blink``
|
||||
* ``reverse``
|
||||
* ``conceal``
|
||||
|
||||
A color specification follows one of the the following patterns:
|
||||
|
||||
* ``role=fg``
|
||||
* ``role=fg/bg``
|
||||
* ``role=fg,option,option``
|
||||
* ``role=fg/bg,option,option``
|
||||
|
||||
where ``role`` is the name of a valid color role, ``fg`` is the
|
||||
foreground color, ``bg`` is the background color and each ``option``
|
||||
is one of the color modifying options. Multiple color specifications
|
||||
are then separated by semicolon. For example::
|
||||
|
||||
export DJANGO_COLORS="error=yellow/blue,blink;notice=magenta"
|
||||
|
||||
would specify that errors be displayed using blinking yellow on blue,
|
||||
and notices displayed using magenta. All other color roles would be
|
||||
left uncolored.
|
||||
|
||||
Colors can also be specified by extending a base palette. If you put
|
||||
a palette name in a color specification, all the colors implied by that
|
||||
palette will be loaded. So::
|
||||
|
||||
export DJANGO_COLORS="light;error=yellow/blue,blink;notice=magenta"
|
||||
|
||||
would specify the use of all the colors in the light color palette,
|
||||
*except* for the colors for errors and notices which would be
|
||||
overridden as specified.
|
||||
|
||||
Bash completion
|
||||
---------------
|
||||
|
@ -472,8 +472,8 @@ Fast Failure for Tests
|
||||
----------------------
|
||||
|
||||
The ``test`` subcommand of ``django-admin.py``, and the ``runtests.py`` script
|
||||
used to run Django's own test suite, support a new ``--failfast`` option.
|
||||
When specified, this option causes the test runner to exit after
|
||||
used to run Django's own test suite, support a new ``--failfast`` option.
|
||||
When specified, this option causes the test runner to exit after
|
||||
encountering a failure instead of continuing with the test run.
|
||||
|
||||
Improved localization
|
||||
@ -492,3 +492,10 @@ Added ``readonly_fields`` to ``ModelAdmin``
|
||||
:attr:`django.contrib.admin.ModelAdmin.readonly_fields` has been added to
|
||||
enable non-editable fields in add/change pages for models and inlines. Field
|
||||
and calculated values can be displayed along side editable fields.
|
||||
|
||||
Customizable syntax highlighting
|
||||
--------------------------------
|
||||
|
||||
You can now use the ``DJANGO_COLORS`` environment variable to modify
|
||||
or disable the colors used by ``django-admin.py`` to provide
|
||||
:ref:`syntax highlighting <syntax-coloring>`.
|
||||
|
149
tests/regressiontests/utils/termcolors.py
Normal file
149
tests/regressiontests/utils/termcolors.py
Normal file
@ -0,0 +1,149 @@
|
||||
from unittest import TestCase
|
||||
|
||||
from django.utils.termcolors import parse_color_setting, PALETTES, DEFAULT_PALETTE, LIGHT_PALETTE, DARK_PALETTE, NOCOLOR_PALETTE
|
||||
|
||||
class TermColorTests(TestCase):
|
||||
|
||||
def test_empty_string(self):
|
||||
self.assertEquals(parse_color_setting(''), PALETTES[DEFAULT_PALETTE])
|
||||
|
||||
def test_simple_palette(self):
|
||||
self.assertEquals(parse_color_setting('light'), PALETTES[LIGHT_PALETTE])
|
||||
self.assertEquals(parse_color_setting('dark'), PALETTES[DARK_PALETTE])
|
||||
self.assertEquals(parse_color_setting('nocolor'), None)
|
||||
|
||||
def test_fg(self):
|
||||
self.assertEquals(parse_color_setting('error=green'),
|
||||
dict(PALETTES[NOCOLOR_PALETTE],
|
||||
ERROR={'fg':'green'}))
|
||||
|
||||
def test_fg_bg(self):
|
||||
self.assertEquals(parse_color_setting('error=green/blue'),
|
||||
dict(PALETTES[NOCOLOR_PALETTE],
|
||||
ERROR={'fg':'green', 'bg':'blue'}))
|
||||
|
||||
def test_fg_opts(self):
|
||||
self.assertEquals(parse_color_setting('error=green,blink'),
|
||||
dict(PALETTES[NOCOLOR_PALETTE],
|
||||
ERROR={'fg':'green', 'opts': ('blink',)}))
|
||||
self.assertEquals(parse_color_setting('error=green,bold,blink'),
|
||||
dict(PALETTES[NOCOLOR_PALETTE],
|
||||
ERROR={'fg':'green', 'opts': ('blink','bold')}))
|
||||
|
||||
def test_fg_bg_opts(self):
|
||||
self.assertEquals(parse_color_setting('error=green/blue,blink'),
|
||||
dict(PALETTES[NOCOLOR_PALETTE],
|
||||
ERROR={'fg':'green', 'bg':'blue', 'opts': ('blink',)}))
|
||||
self.assertEquals(parse_color_setting('error=green/blue,bold,blink'),
|
||||
dict(PALETTES[NOCOLOR_PALETTE],
|
||||
ERROR={'fg':'green', 'bg':'blue', 'opts': ('blink','bold')}))
|
||||
|
||||
def test_override_palette(self):
|
||||
self.assertEquals(parse_color_setting('light;error=green'),
|
||||
dict(PALETTES[LIGHT_PALETTE],
|
||||
ERROR={'fg':'green'}))
|
||||
|
||||
def test_override_nocolor(self):
|
||||
self.assertEquals(parse_color_setting('nocolor;error=green'),
|
||||
dict(PALETTES[NOCOLOR_PALETTE],
|
||||
ERROR={'fg': 'green'}))
|
||||
|
||||
def test_reverse_override(self):
|
||||
self.assertEquals(parse_color_setting('error=green;light'), PALETTES[LIGHT_PALETTE])
|
||||
|
||||
def test_multiple_roles(self):
|
||||
self.assertEquals(parse_color_setting('error=green;sql_field=blue'),
|
||||
dict(PALETTES[NOCOLOR_PALETTE],
|
||||
ERROR={'fg':'green'},
|
||||
SQL_FIELD={'fg':'blue'}))
|
||||
|
||||
def test_override_with_multiple_roles(self):
|
||||
self.assertEquals(parse_color_setting('light;error=green;sql_field=blue'),
|
||||
dict(PALETTES[LIGHT_PALETTE],
|
||||
ERROR={'fg':'green'},
|
||||
SQL_FIELD={'fg':'blue'}))
|
||||
|
||||
def test_empty_definition(self):
|
||||
self.assertEquals(parse_color_setting(';'), None)
|
||||
self.assertEquals(parse_color_setting('light;'), PALETTES[LIGHT_PALETTE])
|
||||
self.assertEquals(parse_color_setting(';;;'), None)
|
||||
|
||||
def test_empty_options(self):
|
||||
self.assertEquals(parse_color_setting('error=green,'),
|
||||
dict(PALETTES[NOCOLOR_PALETTE],
|
||||
ERROR={'fg':'green'}))
|
||||
self.assertEquals(parse_color_setting('error=green,,,'),
|
||||
dict(PALETTES[NOCOLOR_PALETTE],
|
||||
ERROR={'fg':'green'}))
|
||||
self.assertEquals(parse_color_setting('error=green,,blink,,'),
|
||||
dict(PALETTES[NOCOLOR_PALETTE],
|
||||
ERROR={'fg':'green', 'opts': ('blink',)}))
|
||||
|
||||
def test_bad_palette(self):
|
||||
self.assertEquals(parse_color_setting('unknown'), None)
|
||||
|
||||
def test_bad_role(self):
|
||||
self.assertEquals(parse_color_setting('unknown='), None)
|
||||
self.assertEquals(parse_color_setting('unknown=green'), None)
|
||||
self.assertEquals(parse_color_setting('unknown=green;sql_field=blue'),
|
||||
dict(PALETTES[NOCOLOR_PALETTE],
|
||||
SQL_FIELD={'fg':'blue'}))
|
||||
|
||||
def test_bad_color(self):
|
||||
self.assertEquals(parse_color_setting('error='), None)
|
||||
self.assertEquals(parse_color_setting('error=;sql_field=blue'),
|
||||
dict(PALETTES[NOCOLOR_PALETTE],
|
||||
SQL_FIELD={'fg':'blue'}))
|
||||
self.assertEquals(parse_color_setting('error=unknown'), None)
|
||||
self.assertEquals(parse_color_setting('error=unknown;sql_field=blue'),
|
||||
dict(PALETTES[NOCOLOR_PALETTE],
|
||||
SQL_FIELD={'fg':'blue'}))
|
||||
self.assertEquals(parse_color_setting('error=green/unknown'),
|
||||
dict(PALETTES[NOCOLOR_PALETTE],
|
||||
ERROR={'fg':'green'}))
|
||||
self.assertEquals(parse_color_setting('error=green/blue/something'),
|
||||
dict(PALETTES[NOCOLOR_PALETTE],
|
||||
ERROR={'fg':'green', 'bg': 'blue'}))
|
||||
self.assertEquals(parse_color_setting('error=green/blue/something,blink'),
|
||||
dict(PALETTES[NOCOLOR_PALETTE],
|
||||
ERROR={'fg':'green', 'bg': 'blue', 'opts': ('blink',)}))
|
||||
|
||||
def test_bad_option(self):
|
||||
self.assertEquals(parse_color_setting('error=green,unknown'),
|
||||
dict(PALETTES[NOCOLOR_PALETTE],
|
||||
ERROR={'fg':'green'}))
|
||||
self.assertEquals(parse_color_setting('error=green,unknown,blink'),
|
||||
dict(PALETTES[NOCOLOR_PALETTE],
|
||||
ERROR={'fg':'green', 'opts': ('blink',)}))
|
||||
|
||||
def test_role_case(self):
|
||||
self.assertEquals(parse_color_setting('ERROR=green'),
|
||||
dict(PALETTES[NOCOLOR_PALETTE],
|
||||
ERROR={'fg':'green'}))
|
||||
self.assertEquals(parse_color_setting('eRrOr=green'),
|
||||
dict(PALETTES[NOCOLOR_PALETTE],
|
||||
ERROR={'fg':'green'}))
|
||||
|
||||
def test_color_case(self):
|
||||
self.assertEquals(parse_color_setting('error=GREEN'),
|
||||
dict(PALETTES[NOCOLOR_PALETTE],
|
||||
ERROR={'fg':'green'}))
|
||||
self.assertEquals(parse_color_setting('error=GREEN/BLUE'),
|
||||
dict(PALETTES[NOCOLOR_PALETTE],
|
||||
ERROR={'fg':'green', 'bg':'blue'}))
|
||||
|
||||
self.assertEquals(parse_color_setting('error=gReEn'),
|
||||
dict(PALETTES[NOCOLOR_PALETTE],
|
||||
ERROR={'fg':'green'}))
|
||||
self.assertEquals(parse_color_setting('error=gReEn/bLuE'),
|
||||
dict(PALETTES[NOCOLOR_PALETTE],
|
||||
ERROR={'fg':'green', 'bg':'blue'}))
|
||||
|
||||
def test_opts_case(self):
|
||||
self.assertEquals(parse_color_setting('error=green,BLINK'),
|
||||
dict(PALETTES[NOCOLOR_PALETTE],
|
||||
ERROR={'fg':'green', 'opts': ('blink',)}))
|
||||
|
||||
self.assertEquals(parse_color_setting('error=green,bLiNk'),
|
||||
dict(PALETTES[NOCOLOR_PALETTE],
|
||||
ERROR={'fg':'green', 'opts': ('blink',)}))
|
@ -9,8 +9,8 @@ from django.utils.functional import SimpleLazyObject
|
||||
|
||||
import timesince
|
||||
import datastructures
|
||||
import dateformat
|
||||
import itercompat
|
||||
|
||||
from decorators import DecoratorFromMiddlewareTests
|
||||
|
||||
# We need this because "datastructures" uses sorted() and the tests are run in
|
||||
@ -28,6 +28,7 @@ __test__ = {
|
||||
}
|
||||
|
||||
from dateformat import *
|
||||
from termcolors import *
|
||||
|
||||
class TestUtilsHtml(TestCase):
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user