Skip to content

Commit 95bb3f8

Browse files
Move colordepth logic to Output implementations.
1 parent 44dfc89 commit 95bb3f8

File tree

9 files changed

+128
-70
lines changed

9 files changed

+128
-70
lines changed

prompt_toolkit/application/application.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -366,15 +366,23 @@ def conditional_pygments_style() -> BaseStyle:
366366
@property
367367
def color_depth(self) -> ColorDepth:
368368
"""
369-
Active :class:`.ColorDepth`.
369+
The active :class:`.ColorDepth`.
370+
371+
The current value is determined as follows:
372+
- If a color depth was given explicitely to this application, use that
373+
value.
374+
- Otherwise, fall back to the color depth that is reported by the
375+
:class:`.Output` implementation. If the :class:`.Output` class was
376+
created using `output.defaults.create_output`, then this value is
377+
coming from the $PROMPT_TOOLKIT_COLOR_DEPTH environment variable.
370378
"""
371379
depth = self._color_depth
372380

373381
if callable(depth):
374-
return depth() or self.output.get_default_color_depth()
382+
depth = depth()
375383

376384
if depth is None:
377-
return self.output.get_default_color_depth()
385+
depth = self.output.get_default_color_depth()
378386

379387
return depth
380388

prompt_toolkit/output/base.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,20 @@ def get_rows_below_cursor_position(self) -> int:
165165

166166
@abstractmethod
167167
def get_default_color_depth(self) -> ColorDepth:
168-
" Get default color depth for this output. "
168+
"""
169+
Get default color depth for this output.
170+
171+
This value will be used if no color depth was explicitely passed to the
172+
`Application`.
173+
174+
.. note::
175+
176+
If the `$PROMPT_TOOLKIT_COLOR_DEPTH` environment variable has been
177+
set, then `outputs.defaults.create_output` will pass this value to
178+
the implementation as the default_color_depth, which is returned
179+
here. (This is not used when the output corresponds to a
180+
prompt_toolkit SSH/Telnet session.)
181+
"""
169182

170183

171184
class DummyOutput(Output):

prompt_toolkit/output/color_depth.py

Lines changed: 11 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
from enum import Enum
33
from typing import Optional
44

5-
from prompt_toolkit.utils import is_dumb_terminal, is_windows
6-
75
__all__ = [
86
"ColorDepth",
97
]
@@ -35,61 +33,26 @@ class ColorDepth(str, Enum):
3533
TRUE_COLOR = DEPTH_24_BIT
3634

3735
@classmethod
38-
def local_default(cls, term: Optional[str] = None) -> "ColorDepth":
36+
def from_env(cls) -> Optional["ColorDepth"]:
3937
"""
40-
Return the default color depth, according to the $TERM value.
41-
42-
We prefer 256 colors almost always, because this is what most terminals
43-
support these days, and is a good default.
38+
Return the color depth if the $PROMPT_TOOLKIT_COLOR_DEPTH environment
39+
variable has been set.
4440
45-
The $PROMPT_TOOLKIT_COLOR_DEPTH environment variable can be used to
46-
override this outcome. This is a way to enforce a certain color depth
47-
in all prompt_toolkit applications.
48-
49-
If no `term` parameter is given, we use the $TERM environment variable.
41+
This is a way to enforce a certain color depth in all prompt_toolkit
42+
applications.
5043
"""
51-
# Take `TERM` value from environment variable if nothing was passed.
52-
if term is None:
53-
term = os.environ.get("TERM")
54-
55-
if is_dumb_terminal(term):
56-
return cls.DEPTH_1_BIT
57-
5844
# Check the `PROMPT_TOOLKIT_COLOR_DEPTH` environment variable.
5945
all_values = [i.value for i in ColorDepth]
6046
if os.environ.get("PROMPT_TOOLKIT_COLOR_DEPTH") in all_values:
6147
return cls(os.environ["PROMPT_TOOLKIT_COLOR_DEPTH"])
6248

63-
return cls.windows_default() if is_windows() else cls.vt100_default(term)
49+
return None
6450

6551
@classmethod
66-
def vt100_default(cls, term: Optional[str] = None) -> "ColorDepth":
67-
"""Return the default color depth for a vt100 terminal, according to the term
68-
value.
69-
70-
Contrary to `local_default`, this method doesn't take the local system into
71-
account.
52+
def default(cls) -> "ColorDepth":
7253
"""
73-
if term is None:
74-
return cls.DEFAULT
75-
76-
if is_dumb_terminal(term):
77-
return cls.DEPTH_1_BIT
78-
79-
if term in ("linux", "eterm-color"):
80-
return cls.DEPTH_4_BIT
81-
82-
return cls.DEFAULT
83-
84-
@classmethod
85-
def windows_default(cls) -> "ColorDepth":
86-
"""Return the default color depth for a windows terminal.
87-
88-
Contrary to `local_default`, this method doesn't take the local system into
89-
account.
54+
Return the default color depth for the default output.
9055
"""
91-
# For now, always use 4 bit color on Windows 10 by default, even when
92-
# vt100 escape sequences with ENABLE_VIRTUAL_TERMINAL_PROCESSING are
93-
# supported. We don't have a reliable way yet to know whether our
94-
# console supports true color or only 4-bit.
95-
return cls.DEPTH_4_BIT
56+
from .defaults import create_output
57+
58+
return create_output().get_default_color_depth()

prompt_toolkit/output/conemu.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
from typing import Any, TextIO
1+
from typing import Any, Optional, TextIO
22

33
from prompt_toolkit.data_structures import Size
44
from prompt_toolkit.renderer import Output
55

6+
from .color_depth import ColorDepth
67
from .vt100 import Vt100_Output
78
from .win32 import Win32Output
89

@@ -27,9 +28,13 @@ class ConEmuOutput:
2728
http://gooseberrycreative.com/cmder/
2829
"""
2930

30-
def __init__(self, stdout: TextIO) -> None:
31-
self.win32_output = Win32Output(stdout)
32-
self.vt100_output = Vt100_Output(stdout, lambda: Size(0, 0))
31+
def __init__(
32+
self, stdout: TextIO, default_color_depth: Optional[ColorDepth] = None
33+
) -> None:
34+
self.win32_output = Win32Output(stdout, default_color_depth=default_color_depth)
35+
self.vt100_output = Vt100_Output(
36+
stdout, lambda: Size(0, 0), default_color_depth=default_color_depth
37+
)
3338

3439
def __getattr__(self, name: str) -> Any:
3540
if name in (

prompt_toolkit/output/defaults.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
)
1010

1111
from .base import Output
12+
from .color_depth import ColorDepth
1213

1314
__all__ = [
1415
"create_output",
@@ -28,6 +29,12 @@ def create_output(
2829
consumed by something other then a terminal, so this is a reasonable
2930
default.)
3031
"""
32+
# Consider TERM and PROMPT_TOOLKIT_COLOR_DEPTH environment variables.
33+
# Notice that PROMPT_TOOLKIT_COLOR_DEPTH value is the default that's used
34+
# if the Application doesn't override it.
35+
term_from_env = get_term_environment_variable()
36+
color_depth_from_env = ColorDepth.from_env()
37+
3138
if stdout is None:
3239
# By default, render to stdout. If the output is piped somewhere else,
3340
# render to stderr.
@@ -51,12 +58,19 @@ def create_output(
5158
from .windows10 import Windows10_Output, is_win_vt100_enabled
5259

5360
if is_win_vt100_enabled():
54-
return cast(Output, Windows10_Output(stdout))
61+
return cast(
62+
Output,
63+
Windows10_Output(stdout, default_color_depth=color_depth_from_env),
64+
)
5565
if is_conemu_ansi():
56-
return cast(Output, ConEmuOutput(stdout))
66+
return cast(
67+
Output, ConEmuOutput(stdout, default_color_depth=color_depth_from_env)
68+
)
5769
else:
58-
return Win32Output(stdout)
70+
return Win32Output(stdout, default_color_depth=color_depth_from_env)
5971
else:
6072
from .vt100 import Vt100_Output
6173

62-
return Vt100_Output.from_pty(stdout, term=get_term_environment_variable())
74+
return Vt100_Output.from_pty(
75+
stdout, term=term_from_env, default_color_depth=color_depth_from_env
76+
)

prompt_toolkit/output/vt100.py

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from prompt_toolkit.data_structures import Size
2727
from prompt_toolkit.output import Output
2828
from prompt_toolkit.styles import ANSI_COLOR_NAMES, Attrs
29+
from prompt_toolkit.utils import is_dumb_terminal
2930

3031
from .color_depth import ColorDepth
3132

@@ -415,6 +416,7 @@ def __init__(
415416
get_size: Callable[[], Size],
416417
term: Optional[str] = None,
417418
write_binary: bool = True,
419+
default_color_depth: Optional[ColorDepth] = None,
418420
) -> None:
419421

420422
assert all(hasattr(stdout, a) for a in ("write", "flush"))
@@ -425,6 +427,7 @@ def __init__(
425427
self._buffer: List[str] = []
426428
self.stdout = stdout
427429
self.write_binary = write_binary
430+
self.default_color_depth = default_color_depth
428431
self._get_size = get_size
429432
self.term = term
430433

@@ -437,7 +440,12 @@ def __init__(
437440
}
438441

439442
@classmethod
440-
def from_pty(cls, stdout: TextIO, term: Optional[str] = None) -> "Vt100_Output":
443+
def from_pty(
444+
cls,
445+
stdout: TextIO,
446+
term: Optional[str] = None,
447+
default_color_depth: Optional[ColorDepth] = None,
448+
) -> "Vt100_Output":
441449
"""
442450
Create an Output class from a pseudo terminal.
443451
(This will take the dimensions by reading the pseudo
@@ -470,7 +478,7 @@ def get_size() -> Size:
470478
pass
471479
return Size(rows=rows or 24, columns=columns or 80)
472480

473-
return cls(stdout, get_size, term=term)
481+
return cls(stdout, get_size, term=term, default_color_depth=default_color_depth)
474482

475483
def get_size(self) -> Size:
476484
return self._get_size()
@@ -685,4 +693,25 @@ def bell(self) -> None:
685693
self.flush()
686694

687695
def get_default_color_depth(self) -> ColorDepth:
688-
return ColorDepth.vt100_default(self.term)
696+
"""
697+
Return the default color depth for a vt100 terminal, according to the
698+
our term value.
699+
700+
We prefer 256 colors almost always, because this is what most terminals
701+
support these days, and is a good default.
702+
"""
703+
if self.default_color_depth is not None:
704+
return self.default_color_depth
705+
706+
term = self.term
707+
708+
if term is None:
709+
return ColorDepth.DEFAULT
710+
711+
if is_dumb_terminal(term):
712+
return ColorDepth.DEPTH_1_BIT
713+
714+
if term in ("linux", "eterm-color"):
715+
return ColorDepth.DEPTH_4_BIT
716+
717+
return ColorDepth.DEFAULT

prompt_toolkit/output/win32.py

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
windll,
1111
)
1212
from ctypes.wintypes import DWORD, HANDLE
13-
from typing import Dict, List, TextIO, Tuple
13+
from typing import Dict, List, Optional, TextIO, Tuple
1414

1515
from prompt_toolkit.data_structures import Size
1616
from prompt_toolkit.renderer import Output
@@ -85,8 +85,14 @@ class Win32Output(Output):
8585
(cmd.exe and similar.)
8686
"""
8787

88-
def __init__(self, stdout: TextIO, use_complete_width: bool = False) -> None:
88+
def __init__(
89+
self,
90+
stdout: TextIO,
91+
use_complete_width: bool = False,
92+
default_color_depth: Optional[ColorDepth] = None,
93+
) -> None:
8994
self.use_complete_width = use_complete_width
95+
self.default_color_depth = default_color_depth
9096

9197
self._buffer: List[str] = []
9298
self.stdout = stdout
@@ -480,7 +486,21 @@ def win32_refresh_window(cls) -> None:
480486
windll.user32.RedrawWindow(handle, None, None, c_uint(RDW_INVALIDATE))
481487

482488
def get_default_color_depth(self) -> ColorDepth:
483-
return ColorDepth.windows_default()
489+
"""
490+
Return the default color depth for a windows terminal.
491+
492+
Contrary to the Vt100 implementation, this doesn't depend on a $TERM
493+
variable.
494+
"""
495+
if self.default_color_depth is not None:
496+
return self.default_color_depth
497+
498+
# For now, by default, always use 4 bit color on Windows 10 by default,
499+
# even when vt100 escape sequences with
500+
# ENABLE_VIRTUAL_TERMINAL_PROCESSING are supported. We don't have a
501+
# reliable way yet to know whether our console supports true color or
502+
# only 4-bit.
503+
return ColorDepth.DEPTH_4_BIT
484504

485505

486506
class FOREGROUND_COLOR:

prompt_toolkit/output/windows10.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
from ctypes import byref, windll
22
from ctypes.wintypes import DWORD, HANDLE
3-
from typing import Any, TextIO
3+
from typing import Any, Optional, TextIO
44

55
from prompt_toolkit.data_structures import Size
66
from prompt_toolkit.renderer import Output
77
from prompt_toolkit.utils import is_windows
88
from prompt_toolkit.win32_types import STD_OUTPUT_HANDLE
99

10+
from .color_depth import ColorDepth
1011
from .vt100 import Vt100_Output
1112
from .win32 import Win32Output
1213

@@ -24,9 +25,13 @@ class Windows10_Output:
2425
Windows 10 output abstraction. This enables and uses vt100 escape sequences.
2526
"""
2627

27-
def __init__(self, stdout: TextIO) -> None:
28-
self.win32_output = Win32Output(stdout)
29-
self.vt100_output = Vt100_Output(stdout, lambda: Size(0, 0))
28+
def __init__(
29+
self, stdout: TextIO, default_color_depth: Optional[ColorDepth] = None
30+
) -> None:
31+
self.win32_output = Win32Output(stdout, default_color_depth=default_color_depth)
32+
self.vt100_output = Vt100_Output(
33+
stdout, lambda: Size(0, 0), default_color_depth=default_color_depth
34+
)
3035
self._hconsole = HANDLE(windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE))
3136

3237
def flush(self) -> None:

prompt_toolkit/utils.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
"is_conemu_ansi",
2727
"is_windows",
2828
"in_main_thread",
29+
"get_term_environment_variable",
2930
"take_using_weights",
3031
"to_str",
3132
"to_int",

0 commit comments

Comments
 (0)