Merge pull request #353 from LqdBcnAtWork/patch-1
Spelling correction
diff --git a/README.rst b/README.rst
index fb0b75f..7b173d8 100644
--- a/README.rst
+++ b/README.rst
@@ -55,7 +55,8 @@
colored terminal text from Python, and has the happy side-effect that existing
applications or libraries which use ANSI sequences to produce colored output on
Linux or Macs can now also work on Windows, simply by calling
-``colorama.init()``.
+``colorama.just_fix_windows_console()`` (since v0.4.6) or ``colorama.init()``
+(all versions, but may have other side-effects – see below).
An alternative approach is to install ``ansi.sys`` on Windows machines, which
provides the same behaviour for all applications running in terminals. Colorama
@@ -85,30 +86,66 @@
Initialisation
..............
-Applications should initialise Colorama using:
+If the only thing you want from Colorama is to get ANSI escapes to work on
+Windows, then run:
+
+.. code-block:: python
+
+ from colorama import just_fix_windows_console
+ just_fix_windows_console()
+
+If you're on a recent version of Windows 10 or better, and your stdout/stderr
+are pointing to a Windows console, then this will flip the magic configuration
+switch to enable Windows' built-in ANSI support.
+
+If you're on an older version of Windows, and your stdout/stderr are pointing to
+a Windows console, then this will wrap ``sys.stdout`` and/or ``sys.stderr`` in a
+magic file object that intercepts ANSI escape sequences and issues the
+appropriate Win32 calls to emulate them.
+
+In all other circumstances, it does nothing whatsoever. Basically the idea is
+that this makes Windows act like Unix with respect to ANSI escape handling.
+
+It's safe to call this function multiple times. It's safe to call this function
+on non-Windows platforms, but it won't do anything. It's safe to call this
+function when one or both of your stdout/stderr are redirected to a file – it
+won't do anything to those streams.
+
+Alternatively, you can use the older interface with more features (but also more
+potential footguns):
.. code-block:: python
from colorama import init
init()
-On Windows, calling ``init()`` will filter ANSI escape sequences out of any
-text sent to ``stdout`` or ``stderr``, and replace them with equivalent Win32
-calls.
+This does the same thing as ``just_fix_windows_console``, except for the
+following differences:
-On other platforms, calling ``init()`` has no effect (unless you request other
-optional functionality, see "Init Keyword Args" below; or if output
-is redirected). By design, this permits applications to call ``init()``
-unconditionally on all platforms, after which ANSI output should just work.
+- It's not safe to call ``init`` multiple times; you can end up with multiple
+ layers of wrapping and broken ANSI support.
-On all platforms, if output is redirected, ANSI escape sequences are completely
-stripped out.
+- Colorama will apply a heuristic to guess whether stdout/stderr support ANSI,
+ and if it thinks they don't, then it will wrap ``sys.stdout`` and
+ ``sys.stderr`` in a magic file object that strips out ANSI escape sequences
+ before printing them. This happens on all platforms, and can be convenient if
+ you want to write your code to emit ANSI escape sequences unconditionally, and
+ let Colorama decide whether they should actually be output. But note that
+ Colorama's heuristic is not particularly clever.
+
+- ``init`` also accepts explicit keyword args to enable/disable various
+ functionality – see below.
To stop using Colorama before your program exits, simply call ``deinit()``.
This will restore ``stdout`` and ``stderr`` to their original values, so that
Colorama is disabled. To resume using Colorama again, call ``reinit()``; it is
cheaper than calling ``init()`` again (but does the same thing).
+Most users should depend on ``colorama >= 0.4.6``, and use
+``just_fix_windows_console``. The old ``init`` interface will be supported
+indefinitely for backwards compatibility, but we don't plan to fix any issues
+with it, also for backwards compatibility.
+
Colored Output
..............
@@ -145,11 +182,11 @@
.. code-block:: python
- from colorama import init
+ from colorama import just_fix_windows_console
from termcolor import colored
# use Colorama to make Termcolor work on Windows too
- init()
+ just_fix_windows_console()
# then use Termcolor for all colored text output
print(colored('Hello, World!', 'green', 'on_red'))
diff --git a/colorama/__init__.py b/colorama/__init__.py
index 518ac80..f5cdfbe 100644
--- a/colorama/__init__.py
+++ b/colorama/__init__.py
@@ -1,5 +1,5 @@
# Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file.
-from .initialise import init, deinit, reinit, colorama_text
+from .initialise import init, deinit, reinit, colorama_text, just_fix_windows_console
from .ansi import Fore, Back, Style, Cursor
from .ansitowin32 import AnsiToWin32
diff --git a/colorama/ansitowin32.py b/colorama/ansitowin32.py
index 2060311..abf209e 100644
--- a/colorama/ansitowin32.py
+++ b/colorama/ansitowin32.py
@@ -271,3 +271,7 @@
if params[0] in '02':
winterm.set_title(params[1])
return text
+
+
+ def flush(self):
+ self.wrapped.flush()
diff --git a/colorama/initialise.py b/colorama/initialise.py
index 430d066..d5fd4b7 100644
--- a/colorama/initialise.py
+++ b/colorama/initialise.py
@@ -6,13 +6,27 @@
from .ansitowin32 import AnsiToWin32
-orig_stdout = None
-orig_stderr = None
+def _wipe_internal_state_for_tests():
+ global orig_stdout, orig_stderr
+ orig_stdout = None
+ orig_stderr = None
-wrapped_stdout = None
-wrapped_stderr = None
+ global wrapped_stdout, wrapped_stderr
+ wrapped_stdout = None
+ wrapped_stderr = None
-atexit_done = False
+ global atexit_done
+ atexit_done = False
+
+ global fixed_windows_console
+ fixed_windows_console = False
+
+ try:
+ # no-op if it wasn't registered
+ atexit.unregister(reset_all)
+ except AttributeError:
+ # python 2: no atexit.unregister. Oh well, we did our best.
+ pass
def reset_all():
@@ -55,6 +69,29 @@
sys.stderr = orig_stderr
+def just_fix_windows_console():
+ global fixed_windows_console
+
+ if sys.platform != "win32":
+ return
+ if fixed_windows_console:
+ return
+ if wrapped_stdout is not None or wrapped_stderr is not None:
+ # Someone already ran init() and it did stuff, so we won't second-guess them
+ return
+
+ # On newer versions of Windows, AnsiToWin32.__init__ will implicitly enable the
+ # native ANSI support in the console as a side-effect. We only need to actually
+ # replace sys.stdout/stderr if we're in the old-style conversion mode.
+ new_stdout = AnsiToWin32(sys.stdout, convert=None, strip=None, autoreset=False)
+ if new_stdout.convert:
+ sys.stdout = new_stdout
+ new_stderr = AnsiToWin32(sys.stderr, convert=None, strip=None, autoreset=False)
+ if new_stderr.convert:
+ sys.stderr = new_stderr
+
+ fixed_windows_console = True
+
@contextlib.contextmanager
def colorama_text(*args, **kwargs):
init(*args, **kwargs)
@@ -78,3 +115,7 @@
if wrapper.should_wrap():
stream = wrapper.stream
return stream
+
+
+# Use this for initial setup as well, to reduce code duplication
+_wipe_internal_state_for_tests()
diff --git a/colorama/tests/initialise_test.py b/colorama/tests/initialise_test.py
index 7bbd18f..89f9b07 100644
--- a/colorama/tests/initialise_test.py
+++ b/colorama/tests/initialise_test.py
@@ -3,12 +3,12 @@
from unittest import TestCase, main, skipUnless
try:
- from unittest.mock import patch
+ from unittest.mock import patch, Mock
except ImportError:
- from mock import patch
+ from mock import patch, Mock
from ..ansitowin32 import StreamWrapper
-from ..initialise import init
+from ..initialise import init, just_fix_windows_console, _wipe_internal_state_for_tests
from .utils import osname, replace_by
orig_stdout = sys.stdout
@@ -23,6 +23,7 @@
self.assertNotWrapped()
def tearDown(self):
+ _wipe_internal_state_for_tests()
sys.stdout = orig_stdout
sys.stderr = orig_stderr
@@ -40,6 +41,7 @@
@patch('colorama.initialise.reset_all')
@patch('colorama.ansitowin32.winapi_test', lambda *_: True)
+ @patch('colorama.ansitowin32.enable_vt_processing', lambda *_: False)
def testInitWrapsOnWindows(self, _):
with osname("nt"):
init()
@@ -78,14 +80,6 @@
def testInitWrapOffIncompatibleWithAutoresetOn(self):
self.assertRaises(ValueError, lambda: init(autoreset=True, wrap=False))
- @patch('colorama.ansitowin32.winterm', None)
- @patch('colorama.ansitowin32.winapi_test', lambda *_: True)
- def testInitOnlyWrapsOnce(self):
- with osname("nt"):
- init()
- init()
- self.assertWrapped()
-
@patch('colorama.win32.SetConsoleTextAttribute')
@patch('colorama.initialise.AnsiToWin32')
def testAutoResetPassedOn(self, mockATW32, _):
@@ -122,5 +116,74 @@
self.assertFalse(mockRegister.called)
+class JustFixWindowsConsoleTest(TestCase):
+ def _reset(self):
+ _wipe_internal_state_for_tests()
+ sys.stdout = orig_stdout
+ sys.stderr = orig_stderr
+
+ def tearDown(self):
+ self._reset()
+
+ @patch("colorama.ansitowin32.winapi_test", lambda: True)
+ def testJustFixWindowsConsole(self):
+ if sys.platform != "win32":
+ # just_fix_windows_console should be a no-op
+ just_fix_windows_console()
+ self.assertIs(sys.stdout, orig_stdout)
+ self.assertIs(sys.stderr, orig_stderr)
+ else:
+ def fake_std():
+ # Emulate stdout=not a tty, stderr=tty
+ # to check that we handle both cases correctly
+ stdout = Mock()
+ stdout.closed = False
+ stdout.isatty.return_value = False
+ stdout.fileno.return_value = 1
+ sys.stdout = stdout
+
+ stderr = Mock()
+ stderr.closed = False
+ stderr.isatty.return_value = True
+ stderr.fileno.return_value = 2
+ sys.stderr = stderr
+
+ for native_ansi in [False, True]:
+ with patch(
+ 'colorama.ansitowin32.enable_vt_processing',
+ lambda *_: native_ansi
+ ):
+ self._reset()
+ fake_std()
+
+ # Regular single-call test
+ prev_stdout = sys.stdout
+ prev_stderr = sys.stderr
+ just_fix_windows_console()
+ self.assertIs(sys.stdout, prev_stdout)
+ if native_ansi:
+ self.assertIs(sys.stderr, prev_stderr)
+ else:
+ self.assertIsNot(sys.stderr, prev_stderr)
+
+ # second call without resetting is always a no-op
+ prev_stdout = sys.stdout
+ prev_stderr = sys.stderr
+ just_fix_windows_console()
+ self.assertIs(sys.stdout, prev_stdout)
+ self.assertIs(sys.stderr, prev_stderr)
+
+ self._reset()
+ fake_std()
+
+ # If init() runs first, just_fix_windows_console should be a no-op
+ init()
+ prev_stdout = sys.stdout
+ prev_stderr = sys.stderr
+ just_fix_windows_console()
+ self.assertIs(prev_stdout, sys.stdout)
+ self.assertIs(prev_stderr, sys.stderr)
+
+
if __name__ == '__main__':
main()
diff --git a/colorama/winterm.py b/colorama/winterm.py
index fd7202c..aad867e 100644
--- a/colorama/winterm.py
+++ b/colorama/winterm.py
@@ -190,5 +190,6 @@
mode = win32.GetConsoleMode(handle)
if mode & win32.ENABLE_VIRTUAL_TERMINAL_PROCESSING:
return True
- except OSError:
+ # Can get TypeError in testsuite where 'fd' is a Mock()
+ except (OSError, TypeError):
return False
diff --git a/demos/demo01.py b/demos/demo01.py
index 99d896a..c367024 100644
--- a/demos/demo01.py
+++ b/demos/demo01.py
@@ -10,9 +10,9 @@
# Add parent dir to sys path, so the following 'import colorama' always finds
# the local source in preference to any installed version of colorama.
import fixpath
-from colorama import init, Fore, Back, Style
+from colorama import just_fix_windows_console, Fore, Back, Style
-init()
+just_fix_windows_console()
# Fore, Back and Style are convenience classes for the constant ANSI strings that set
# the foreground, background and style. The don't have any magic of their own.
diff --git a/demos/demo02.py b/demos/demo02.py
index ea96d87..8ca7d4b 100644
--- a/demos/demo02.py
+++ b/demos/demo02.py
@@ -5,9 +5,9 @@
from __future__ import print_function
import fixpath
-from colorama import init, Fore, Back, Style
+from colorama import just_fix_windows_console, Fore, Back, Style
-init()
+just_fix_windows_console()
print(Fore.GREEN + 'green, '
+ Fore.RED + 'red, '
diff --git a/demos/demo06.py b/demos/demo06.py
index 2213be7..1d52c1b 100644
--- a/demos/demo06.py
+++ b/demos/demo06.py
@@ -24,7 +24,7 @@
PASSES = 1000
def main():
- colorama.init()
+ colorama.just_fix_windows_console()
pos = lambda y, x: Cursor.POS(x, y)
# draw a white border.
print(Back.WHITE, end='')
diff --git a/demos/demo07.py b/demos/demo07.py
index f569580..0d28a1e 100644
--- a/demos/demo07.py
+++ b/demos/demo07.py
@@ -16,7 +16,7 @@
aba
3a4
"""
- colorama.init()
+ colorama.just_fix_windows_console()
print("aaa")
print("aaa")
print("aaa")