Code

Opened 4 years ago

Closed 4 years ago

Last modified 4 years ago

#12112 closed (fixed)

Allow terminal colors to be customized or disabled

Reported by: russellm Owned by: nobody
Component: Core (Management commands) Version: 1.1
Severity: Keywords:
Cc: Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: UI/UX:

Description

Some management commands already use colorization to improve usability of output - e.g., validation errors are in red; SQL is pretty-printed in color. #7679 proposes adding yet more color.

However, colored output can pose problems for anyone with color blindness or other vision problems.

The simple choice of colors can also be problematic - red/blue on black is hard to read; yellow on white is also hard to read.

We should provide a way to customize the colors that are used, or disable the use of color entirely.

Attachments (0)

Change History (6)

comment:1 Changed 4 years ago by russellm

  • Component changed from Uncategorized to django-admin.py
  • Needs documentation unset
  • Needs tests unset
  • Patch needs improvement unset
  • Triage Stage changed from Unreviewed to Accepted

Some initial proposals, and possible problems:

  1. Introduce a --nocolor option to all management commands. This is slightly cumbersome to use for those that need to, but is the simplest to implement
  2. Introduce a setting for colorization; USE_TERMINAL_COLORS=True by default, but allow users to override. This is a problem for django-admin.py, which may not have a settings file.
  3. Use environment variables. export DJANGO_NOCOLOR; if DJANGO_NOCOLOR exists in the environment, no color is used. However, it introduces a dependency on the system environment.

Suggestions welcome.

comment:2 Changed 4 years ago by anonymous

I've been thinking about this one, and I think the best solution is to have a .djangorc file in the user's home directory that contains all the color settings. Sort of like .gitrc and .hgrc and the like. I don't think the colors belong in the settings.py file because it's not really a project-wide setting. You can have multiple users working on the same project, each with their own terminal settings.

Is it completely unheard of for a python module like django to take settings from an .*rc file? How do other frameworks (RoR?) deal with this problem?

comment:3 Changed 4 years ago by russellm

djangorc files have a few problems.

  1. The aren't easy to make cross platform (Windows being the black duck),
  2. If we have a djangorc file, we'll start to use it, which could lead to interesting bugs that only occur because of some setting someone has in their rc file.
  3. We've managed to survive a long time without any need for global configuration; color really is the first time this has been needed.

So Jacob has confirmed an environment variable as the way to go for this.

Here's a first pass at a spec for someone to code. This is broadly drawn from the way LS_COLORS works, but is more human-readable:

Colorization will be configurable using a DJANGO_COLORS environment variable.

Django will ship with 3 built-in 'palettes': light, dark and nocolor.

DJANGO_COLORS=dark

would select the dark palette (i.e., colors that can be seen on a black background). Current default colors correspond to the light palette (colors that can be seen on a white background). Patch author gets to pick the color of the bikeshed (literally) for the dark palette, but I reserve editorial judgement for the core if you pick light puce on mushroom :-)

As well as selecting a predefined palette, colors can be individually specified:

DJANGO_COLORS="error=red,bold;warn=blue/white,flashing;notice=green"

This would define errors as bold red, warnings as flashing blue on a white background, notices as simple green. (This particular use case wins no points for aesthetics, but demonstrates syntax).

The 'error', 'warn', 'notice' flags here are the styles that are currently defined in django/core/management/color.py. These styles may require some cleanup to make them appropriate for configuration use.

The color names and properties like flash/bold are defined in django/utils/termcolors.py.

If DJANGO_COLORS doesn't specify a color for a particular style, colors should fall back to the light palette.

If the user wants to specify a different default palette, that should be the first entry in DJANGO_COLORS. For example:

DJANGO_COLORS="dark;error=magenta,bold"

This would result in the standard dark palette, but with errors bold magenta.

Existing 'supports_color' logic must to be preserved. Parsing of the environment variable must be robust -i.e., no runtime exceptions. If a DJANGO_COLORS definition is unparseable, it should fall back to no colors at all.

As a guide to implementation - if anything in this proposal proves to be impractical or impossible, I'm open to revisions. The goal here is to get easily customizable colors; I'm not hung up on any particular detail beyond the basics.

This will obviously require some sort of parser for the environment variable, and this parser will need to have lots of tests as part of the patch. However, there's no real way to test that the colors will be correctly applied in normal use, so it's ok to leave the final bit (e.g., checking that manage.py sqlall actually writes in color) as an eyeball test.

comment:4 Changed 4 years ago by russellm

  • Resolution set to fixed
  • Status changed from new to closed

(In [12009]) Fixed #12112 -- Made the colors used by syntax highlighting customizable.

comment:5 Changed 4 years ago by nbv4

Honestly, I think a better solution is to bypass the session variable malarky and just do it like:

./manage.py runserver --color=dark

or maybe a line in manage.py where you can set a variable which will set the color for all commands ran through manage.py

comment:6 Changed 4 years ago by hunteke

It's going to be hard to change momentum since [12009], nor do I necessarily think the Django devs should, but I offer this as a suggestion or "data point" in response to nbv4.

This is all by way of saying that one could (and I currently do) use a variable set in a settings file to decide whether to color the console output, that does not affect anyone but me. Further, I can store it in the repo so others can learn about a setting (or anything) they may not typically use.

I typically develop on my laptop, for a server environment. Meaning: settings need to be both global and local (for example, I have a different DB setup, nor do I personally need to load some modules of code that I'm not actively developing). Until we're educated to the Better Way, this is the logic that enables us to have per-machine knobs twisted while maintaining global settings.

Our solution was to use a settings/ directory. This allows us to have multiple settings files that don't pollute the directory structure, and allows us to have to-the-point settings files for different environments. Once we did that, the __init__.py was mandatory, and the rest fell into place.

NB: asterisks (*) put in to perhaps make the command line actions bold in some email clients.

*$ find  proj_dir/ -print  |  grep  settings  |  grep -v ".pyc$"*
proj_dir/settings
proj_dir/settings/__init__.py
proj_dir/settings/common_settings.py
proj_dir/settings/kevin_hani.py
proj_dir/settings/kevin_lina.py
proj_dir/settings/passwords.py
proj_dir/settings/passwords-sample.py

*$ cat proj_dir/settings/__init__.py*
import getpass, socket, sys, logging

try:
   user = getpass.getuser()       # or $ whoami   (cmdline)
   host = socket.gethostname()    # or $ hostname (cmdline)
   import_settings = 'from %s_%s import *' % (user, host)

   exec( import_settings )  # Load machine-specific settings

except ImportError, e:
   logging.error( ' Could not import necessary file:\n---\n %s' % e )

try:
   from passwords import *  # ... then load sensitive, non-Git-watched pwds.
except ImportError, e:
   msg  = ' Could not import passwords.py.\n---\n'
   msg += 'Have you set up a database account?\n'
   msg += 'Have you copied settings/passwords-sample.py to\n'
   msg += 'settings/passwords.py, with appropriate DB credentials?\n'
   logging.error( msg )
   sys.exit()


*$ cat kevin_hani.py*
from common_settings import *  # First, import all shared settings ...

[... then do any further specific machine settings necessary ...]

Add Comment

Modify Ticket

Change Properties
<Author field>
Action
as closed
as The resolution will be set. Next status will be 'closed'
The resolution will be deleted. Next status will be 'new'
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.