diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
index 4a65b6c..fc44d47 100644
--- a/.github/FUNDING.yml
+++ b/.github/FUNDING.yml
@@ -9,4 +9,6 @@
# open_collective: # Replace with a single Open Collective username
# otechie: # Replace with a single Otechie username
# patreon: # Replace with a single Patreon username
+custom: ["https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=2MZ9D2GMLYCUJ&item_name=Colorama¤cy_code=USD"]
tidelift: "pypi/colorama"
+
diff --git a/.gitignore b/.gitignore
index 202551b..b8d794f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,11 +2,12 @@
*.egg-info
.coverage
.tox/
-MANIFEST
-dist
-tags
+/MANIFEST
+/build/
+/dist/
+/sandbox/
+/tags
virtualenv
-build
# PyCharm
.idea
diff --git a/.travis.yml b/.travis.yml
index 7e11b00..706b55c 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -3,6 +3,7 @@ cache: pip
matrix:
include:
- python: 2.7
+ - python: 3.9
- python: 3.8
- arch: arm64
python: 3.7
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index f88f449..555941c 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -1,6 +1,23 @@
+0.4.6
+ * Support reverse video and underline on Windows 10
+0.4.5 In progress, unreleased
+ * Create README-hacking.md, for Colorama contributors.
+ * Tweak some README unicode characters that don't render correctly on PyPI.
+ * Fix some tests that were failing on some operating systems.
+ * Add support for Python 3.9.
+0.4.4 Current release
+ * Re-org of README, to put the most insteresting parts near the top.
+ * Added Linux makefile targets and Windows powershell scripts to
+ automate bootstrapping a development environment, and automate the
+ process of testing wheels before they are uploaded to PyPI.
+ * Use stdlib unittest.mock where available
+ * Travis CI now also builds on arm64
+ * Demo06 demonstrates existing cursor positioning feature
+ * Fix OSC regex & handling to prevent hang or crash
+ * Document enterprise support by Tidelift
0.4.3
* Fix release 0.4.2 which was uploaded with missing files.
-0.4.2
+0.4.2 BROKEN DO NOT USE
* #228: Drop support for EOL Python 3.4, and add 3.7 and 3.8.
Thanks to hugovk.
* Several additions and fixes to documentation and metadata.
diff --git a/Makefile b/Makefile
index 312bd5e..a0b8987 100644
--- a/Makefile
+++ b/Makefile
@@ -10,24 +10,56 @@ help: ## Display help for documented make targets.
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \
awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-7s\033[0m %s\n", $$1, $$2}'
-clean: ## Remove build artifacts and .pyc files
- -rm -rf build dist MANIFEST colorama.egg-info
+
+# bootstrap environment
+
+virtualenv=~/.virtualenvs/colorama
+pip=$(virtualenv)/bin/pip
+syspython=python3.8
+python=$(virtualenv)/bin/python
+twine=$(virtualenv)/bin/twine
+version=$(shell $(python) setup.py --version)
+
+clean: ## Remove build artifacts, .pyc files, virtualenv
+ -rm -rf build dist MANIFEST colorama.egg-info $(virtualenv)
-find . -type f -name '*.py[co]' -delete -o -type d -name __pycache__ -delete
.PHONY: clean
-build: clean ## Build an sdist and wheel
- python setup.py sdist bdist_wheel
-.PHONY: sdist
+$(virtualenv):
+ $(syspython) -m venv --clear $(virtualenv)
+ $(pip) install --upgrade pip
-upload: ## Upload our sdist and wheel
- twine upload dist/*
-.PHONY: release
+venv: $(virtualenv) ## Create or clear a virtualenv
+.PHONY: venv
+
+bootstrap: venv ## Populate the virtualenv
+ $(pip) install -r requirements.txt -r requirements-dev.txt
+.PHONY: bootstrap
-test: ## Run tests
- python -m unittest discover -p *_test.py
-.PHONY: test
+
+# development
tags: ## Create tags file
ctags -R ${NAME}
.PHONY: tags
+test: ## Run tests
+ $(python) -m unittest discover -p *_test.py
+.PHONY: test
+
+
+# build packages
+
+build: ## Build a release (sdist and wheel)
+ $(python) -m pip install --upgrade setuptools wheel
+ $(python) setup.py sdist bdist_wheel
+.PHONY: build
+
+test-release: build ## Test a built release
+ ./test-release
+.PHONY: test-release
+
+release: ## Upload a built release
+ $(twine) upload dist/colorama-$(version)*{.whl,.tar.gz}
+.PHONY: release
+
diff --git a/README-hacking.md b/README-hacking.md
new file mode 100644
index 0000000..8cc5b55
--- /dev/null
+++ b/README-hacking.md
@@ -0,0 +1,83 @@
+# Colorama Development
+
+Help and fixes are welcome!
+
+Although Colorama has no requirements other than the Python standard library,
+development requires some Python packages, which are captured in
+requirements-dev.txt.
+
+Throughout, if you're on a Mac, you can probably do something similar to the
+Linux instructions. Either use the makefile directly, or look in it to see
+what commands it executes, and manually execute something similar. PRs to
+automate for Mac appreciated! Especially if they just made the existing Linux
+Makefile targets work on Mac too.
+
+## Makefile and PowerShell scripts
+
+Some common commands are captured as Linux makefile targets (which could
+perhaps be coaxed into running on OSX in Bash), and as Windows PowerShell
+scripts.
+
+| Task | Linux | Windows |
+|---------------------------------|---------------------|----------------------|
+| Create & populate virtualenv. | `make bootstrap` | `.\bootstrap.ps1` |
+| Run tests. | `make test` | `.\test.ps1` |
+| Build a wheel. | `make build` | `.\build.ps1` |
+| Test the wheel. | `make test-release` | `.\test-release.ps1` |
+| Release the wheel on PyPI | `make release` | `.\release.ps1` |
+| Clean generated files & builds. | `make clean` | `.\clean.ps1` |
+
+The Makefile is self-documenting, so 'make' with no args will describe each
+target.
+
+## Release checklist
+
+1. Check the CHANGELOG is updated with everything since the last release.
+2. Remove the '-pre' suffix from `__version__` in `colorama/__init.py__.py`.
+3. Run the tests locally on your preferred OS, just to save you from doing
+ the following time-consuming steps while there are still obvious problems
+ in the code:
+
+ * Windows: `./test.ps1`
+ * Linux: `make test`
+
+4. Verify you're all committed, merged to master, and pushed to origin (This
+ triggers a Travis build, which we'll check later on)
+
+5. Build the distributables (sdist and wheel), on either OS:
+
+ * Windows: `.\build.ps1`
+ * Linux: `make build`
+
+6. Test the distributables on both OS. Whichever one you do 2nd will get an
+ HTTP 400 response on uploading to test.pypi.org, but outputs a message
+ saying this is expected and carries on:
+
+ * Windows: `./clean.ps1 && .\bootstrap.ps1 && .\build.ps1 &&
+ .\test-release.ps1`
+ * Linux: `make clean bootstrap build test-release`
+
+ (This currently only tests the wheel, but
+ [should soon test the sdist too](https://github.com/tartley/colorama/issues/286).)
+
+7. Check the [Travis builds](https://travis-ci.org/github/tartley/colorama)
+ are complete and all passing. (This currently only tests on Linux, but
+ [should soon run on Windows too](https://github.com/tartley/colorama/issues/283).)
+
+8. Upload the distributables to PyPI:
+
+ * On Windows: `.\release.ps1`
+ * On Linux: `make release`
+
+ This [should soon tag the release for you](https://github.com/tartley/colorama/issues/282). Until then:
+
+9. Tag the current commit with the `__version__` from `colorama/__init__.py`.
+ We should start using
+ [annotated tags for releases](https://www.tartley.com/posts/til-git-annotated-tags/), so:
+
+ git tag -a -m "" $version
+ git push --follow-tags
+
+10. Bump the version number in `colorama/__init__.py`, and add the '-pre'
+ suffix again, ready for the next release. Commit and push this (directly to
+ master is fine.)
diff --git a/README.rst b/README.rst
index 9a5f7d0..e03e51f 100644
--- a/README.rst
+++ b/README.rst
@@ -10,19 +10,41 @@
:target: https://travis-ci.org/tartley/colorama
:alt: Build Status
-Download and docs:
- https://pypi.org/project/colorama/
-Source code & Development:
- https://github.com/tartley/colorama
-Colorama for Enterprise:
- https://github.com/tartley/colorama/blob/master/ENTERPRISE.md
-Description
-===========
+Colorama
+========
Makes ANSI escape character sequences (for producing colored terminal text and
cursor positioning) work under MS Windows.
+.. |donate| image:: https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif
+ :target: https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=2MZ9D2GMLYCUJ&item_name=Colorama¤cy_code=USD
+ :alt: Donate with Paypal
+
+`PyPI for releases `_ |
+`Github for source `_ |
+`Colorama for enterprise on Tidelift `_
+
+If you find Colorama useful, please |donate| to the authors. Thank you!
+
+
+Installation
+------------
+
+Tested on CPython 2.7, 3.5, 3.6, 3.7, 3.8 and 3.9 and Pypy 2.7.
+
+No requirements other than the standard library.
+
+.. code-block:: bash
+
+ pip install colorama
+ # or
+ conda install -c anaconda colorama
+
+
+Description
+-----------
+
ANSI escape character sequences have long been used to produce colored terminal
text and cursor positioning on Unix and Macs. Colorama makes this work on
Windows, too, by wrapping ``stdout``, stripping ANSI sequences it finds (which
@@ -30,11 +52,6 @@ would appear as gobbledygook in the output), and converting them into the
appropriate win32 calls to modify the state of the terminal. On other platforms,
Colorama does nothing.
-Colorama also provides some shortcuts to help generate ANSI sequences
-but works fine in conjunction with any other ANSI sequence generation library,
-such as the venerable Termcolor (https://pypi.org/project/termcolor/)
-or the fabulous Blessings (https://pypi.org/project/blessings/).
-
This has the upshot of providing a simple cross-platform API for printing
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
@@ -60,26 +77,14 @@ handling, versus on Windows Command-Prompt using Colorama:
:height: 325
:alt: Same ANSI sequences on Windows, using Colorama.
-These screengrabs show that, on Windows, Colorama does not support ANSI 'dim
+These screenshots show that, on Windows, Colorama does not support ANSI 'dim
text'; it looks the same as 'normal text'.
-
-License
-=======
-
-Copyright Jonathan Hartley & Arnon Yaari, 2013. BSD 3-Clause license; see LICENSE file.
-
-
-Dependencies
-============
-
-None, other than Python. Tested on Python 2.7, 3.5, 3.6, 3.7 and 3.8.
-
Usage
-=====
+-----
Initialisation
---------------
+..............
Applications should initialise Colorama using:
@@ -97,14 +102,14 @@ optional functionality; see "Init Keyword Args", below). By design, this permits
applications to call ``init()`` unconditionally on all platforms, after which
ANSI output should just work.
-To stop using colorama before your program exits, simply call ``deinit()``.
+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).
Colored Output
---------------
+..............
Cross-platform printing of colored text can then be done using Colorama's
constant shorthand for ANSI escape sequences:
@@ -125,8 +130,10 @@ constant shorthand for ANSI escape sequences:
print('\033[31m' + 'some red text')
print('\033[39m') # and reset to default color
-...or, Colorama can be used happily in conjunction with existing ANSI libraries
-such as Termcolor:
+...or, Colorama can be used in conjunction with existing ANSI libraries
+such as the venerable `Termcolor `_
+or the fabulous `Blessings `_.
+This is highly recommended for anything more than trivial coloring:
.. code-block:: python
@@ -150,14 +157,14 @@ perform this reset automatically on program exit.
Cursor Positioning
-------------------
+..................
ANSI codes to reposition the cursor are supported. See ``demos/demo06.py`` for
an example of how to generate them.
Init Keyword Args
------------------
+.................
``init()`` accepts some ``**kwargs`` to override default behaviour.
@@ -174,7 +181,7 @@ init(autoreset=False):
print('automatically back to default color again')
init(strip=None):
- Pass ``True`` or ``False`` to override whether ansi codes should be
+ Pass ``True`` or ``False`` to override whether ANSI codes should be
stripped from the output. The default behaviour is to strip if on Windows
or if output is redirected (not a tty).
@@ -184,7 +191,7 @@ init(convert=None):
and output is to a tty (terminal).
init(wrap=True):
- On Windows, colorama works by replacing ``sys.stdout`` and ``sys.stderr``
+ On Windows, Colorama works by replacing ``sys.stdout`` and ``sys.stderr``
with proxy objects, which override the ``.write()`` method to do their work.
If this wrapping causes you problems, then this can be disabled by passing
``init(wrap=False)``. The default behaviour is to wrap if ``autoreset`` or
@@ -208,39 +215,10 @@ init(wrap=True):
print(Fore.BLUE + 'blue text on stderr', file=stream)
-Installation
-=======================
-colorama is currently installable from PyPI:
-
- pip install colorama
-
-colorama also can be installed by the conda package manager:
-
- conda install -c anaconda colorama
-
-
-Status & Known Problems
-=======================
-
-I've personally only tested it on Windows XP (CMD, Console2), Ubuntu
-(gnome-terminal, xterm), and OS X.
-
-Some presumably valid ANSI sequences aren't recognised (see details below),
-but to my knowledge nobody has yet complained about this. Puzzling.
-
-See outstanding issues and wishlist:
-https://github.com/tartley/colorama/issues
-
-If anything doesn't work for you, or doesn't do what you expected or hoped for,
-I'd love to hear about it on that issues list, would be delighted by patches,
-and would be happy to grant commit access to anyone who submits a working patch
-or two.
-
-
Recognised ANSI Sequences
-=========================
+.........................
-ANSI sequences generally take the form:
+ANSI sequences generally take the form::
ESC [ ; ...
@@ -249,7 +227,7 @@ more params are passed to a ````. If no params are passed, it is
generally synonymous with passing a single zero. No spaces exist in the
sequence; they have been inserted here simply to read more easily.
-The only ANSI sequences that colorama converts into win32 calls are::
+The only ANSI sequences that Colorama converts into win32 calls are::
ESC [ 0 m # reset all (colors and brightness)
ESC [ 1 m # bright
@@ -306,29 +284,37 @@ them though. Let me know if it would be useful for you, via the Issues on
GitHub.
-Development
-===========
+Status & Known Problems
+-----------------------
+
+I've personally only tested it on Windows XP (CMD, Console2), Ubuntu
+(gnome-terminal, xterm), and OS X.
+
+Some presumably valid ANSI sequences aren't recognised (see details below),
+but to my knowledge nobody has yet complained about this. Puzzling.
-Help and fixes welcome!
+See outstanding issues and wish-list:
+https://github.com/tartley/colorama/issues
-Running tests requires:
+If anything doesn't work for you, or doesn't do what you expected or hoped for,
+I'd love to hear about it on that issues list, would be delighted by patches,
+and would be happy to grant commit access to anyone who submits a working patch
+or two.
-- Michael Foord's ``mock`` module to be installed on Python < 3.3.
-- Tests are written using 2010-era updates to ``unittest``
+If you're hacking on the code, see `README-hacking.md`_.
-To run tests::
+.. _README-hacking.md: README-hacking.md
- python -m unittest discover -p *_test.py
-This, like a few other handy commands, is captured in a ``Makefile``.
+License
+-------
-If you use nose to run the tests, you must pass the ``-s`` flag; otherwise,
-``nosetests`` applies its own proxy to ``stdout``, which confuses the unit
-tests.
+Copyright Jonathan Hartley & Arnon Yaari, 2013-2020. BSD 3-Clause license; see
+LICENSE file.
Professional support
-====================
+--------------------
.. |tideliftlogo| image:: https://cdn2.hubspot.net/hubfs/4008838/website/logos/logos_for_download/Tidelift_primary-shorthand-logo.png
:alt: Tidelift
@@ -349,7 +335,8 @@ Professional support
Thanks
-======
+------
+
* Marc Schlaich (schlamar) for a ``setup.py`` fix for Python2.5.
* Marc Abramowitz, reported & fixed a crash on exit with closed ``stdout``,
providing a solution to issue #7's setuptools/distutils debate,
diff --git a/bootstrap.ps1 b/bootstrap.ps1
new file mode 100644
index 0000000..3260f8b
--- /dev/null
+++ b/bootstrap.ps1
@@ -0,0 +1,9 @@
+$syspython="python3.8.exe"
+$ve="$HOME\.virtualenvs\colorama"
+$bin="$ve\Scripts"
+
+echo "Create $syspython virtualenv $ve"
+& $syspython -m venv --clear "$ve"
+& $bin\python.exe -m pip install --upgrade pip
+& $bin\python.exe -m pip install -r requirements.txt -r requirements-dev.txt
+
diff --git a/build.ps1 b/build.ps1
new file mode 100644
index 0000000..a48e357
--- /dev/null
+++ b/build.ps1
@@ -0,0 +1,6 @@
+$ve="$HOME\.virtualenvs\colorama"
+$bin="$ve\Scripts"
+
+& $bin\python.exe -m pip install --upgrade setuptools wheel
+& $bin\python.exe setup.py sdist bdist_wheel
+
diff --git a/clean.ps1 b/clean.ps1
new file mode 100644
index 0000000..de9ba5a
--- /dev/null
+++ b/clean.ps1
@@ -0,0 +1,6 @@
+$syspython="python3.8.exe"
+$ve="$HOME\.virtualenvs\colorama"
+
+remove-item -r -fo * -I build,dist,MANIFEST,colorama.egg-info,$ve,sandbox
+& $syspython -Bc "import pathlib, shutil; [shutil.rmtree(p) for p in pathlib.Path('.').rglob('__pycache__')]"
+
diff --git a/colorama/__init__.py b/colorama/__init__.py
index b149ed7..6502bed 100644
--- a/colorama/__init__.py
+++ b/colorama/__init__.py
@@ -3,4 +3,4 @@
from .ansi import Fore, Back, Style, Cursor
from .ansitowin32 import AnsiToWin32
-__version__ = '0.4.4'
+__version__ = '0.4.6-pre'
diff --git a/colorama/ansi.py b/colorama/ansi.py
index 11ec695..1c8aba9 100644
--- a/colorama/ansi.py
+++ b/colorama/ansi.py
@@ -91,10 +91,17 @@ class AnsiBack(AnsiCodes):
class AnsiStyle(AnsiCodes):
- BRIGHT = 1
- DIM = 2
- NORMAL = 22
- RESET_ALL = 0
+ BRIGHT = 1
+ DIM = 2
+ BRIGHT_OFF = 21 # Produces double-underline on some terminals.
+ NORMAL = 22
+ RESET_ALL = 0
+
+ UNDERLINE = 4
+ UNDERLINE_OFF = 24
+ REVERSE = 7
+ REVERSE_OFF = 27
+
Fore = AnsiFore()
Back = AnsiBack()
diff --git a/colorama/ansitowin32.py b/colorama/ansitowin32.py
index 6039a05..38abb6d 100644
--- a/colorama/ansitowin32.py
+++ b/colorama/ansitowin32.py
@@ -2,16 +2,21 @@
import re
import sys
import os
+import platform
from .ansi import AnsiFore, AnsiBack, AnsiStyle, Style, BEL
from .winterm import WinTerm, WinColor, WinStyle
-from .win32 import windll, winapi_test
-
+from .win32 import windll, winapi_test, STDOUT, ENABLE_VIRTUAL_TERMINAL_PROCESSING
winterm = None
if windll is not None:
winterm = WinTerm()
+# Track whether we've enabled Win 10 virtual terminal, and
+# what the original mode was set to.
+we_enabled_windows_vt = False
+old_mode_value = None
+
class StreamWrapper(object):
'''
@@ -25,6 +30,7 @@ def __init__(self, wrapped, converter):
self.__wrapped = wrapped
self.__convertor = converter
+
def __getattr__(self, name):
return getattr(self.__wrapped, name)
@@ -71,6 +77,8 @@ class AnsiToWin32(object):
ANSI_OSC_RE = re.compile('\001?\033\\]([^\a]*)(\a)\002?') # Operating System Command
def __init__(self, wrapped, convert=None, strip=None, autoreset=False):
+ global old_mode_value
+ global we_enabled_windows_vt
# The wrapped stream (normally sys.stdout or sys.stderr)
self.wrapped = wrapped
@@ -80,12 +88,38 @@ def __init__(self, wrapped, convert=None, strip=None, autoreset=False):
# create the proxy wrapping our output stream
self.stream = StreamWrapper(wrapped, self)
+ conversion_supported = True
on_windows = os.name == 'nt'
# We test if the WinAPI works, because even if we are on Windows
# we may be using a terminal that doesn't support the WinAPI
# (e.g. Cygwin Terminal). In this case it's up to the terminal
# to support the ANSI codes.
- conversion_supported = on_windows and winapi_test()
+ if on_windows and platform.release().startswith('10'):
+ # Windows 10 supports ANSI sequences using virtual terminal mode.
+ # However, this has been turned off by default from an early Windows 10 version.
+ # We turn virtual terminal support back on, then set
+ # `conversion_supported` to False. Behaviour will then be the same
+ # as for Linux - colorama doesn't use win32 calls.
+ from ctypes import wintypes, byref # import wintypes now we know we're on Windows.
+ old_mode = wintypes.DWORD()
+
+ stdout_handle = windll.kernel32.GetStdHandle(STDOUT)
+ # If the next call failed, we may be running on Windows pre 10.0.10586.
+ # Leave ANSI handling to colorama, there's no virtual terminal support available.
+ if windll.kernel32.GetConsoleMode(stdout_handle, byref(old_mode)):
+ if not (old_mode.value & ENABLE_VIRTUAL_TERMINAL_PROCESSING):
+ if old_mode_value is None:
+ # Don't set this again. __init__ gets called twice, for stdout and stderr.
+ old_mode_value = old_mode.value
+ # Only enable if it's not already enabled
+ if windll.kernel32.SetConsoleMode(stdout_handle, old_mode_value | ENABLE_VIRTUAL_TERMINAL_PROCESSING):
+ we_enabled_windows_vt = True
+
+ conversion_supported = False
+
+ # If not on Windows 10, we have colorama's previous behaviour.
+ # Make sure we don't stomp on `conversion_supported` if it was set False above.
+ conversion_supported = conversion_supported and on_windows and winapi_test()
# should we strip ANSI sequences from our output?
if strip is None:
@@ -120,6 +154,13 @@ def get_win32_calls(self):
AnsiStyle.BRIGHT: (winterm.style, WinStyle.BRIGHT),
AnsiStyle.DIM: (winterm.style, WinStyle.NORMAL),
AnsiStyle.NORMAL: (winterm.style, WinStyle.NORMAL),
+ AnsiStyle.BRIGHT_OFF: (winterm.style, WinStyle.NORMAL),
+
+ AnsiStyle.UNDERLINE: (winterm.not_implemented, WinStyle.UNDERLINE),
+ AnsiStyle.UNDERLINE_OFF: (winterm.not_implemented, WinStyle.UNDERLINE_OFF),
+ AnsiStyle.REVERSE: (winterm.not_implemented, WinStyle.REVERSE),
+ AnsiStyle.REVERSE_OFF: (winterm.not_implemented, WinStyle.REVERSE_OFF),
+
AnsiFore.BLACK: (winterm.fore, WinColor.BLACK),
AnsiFore.RED: (winterm.fore, WinColor.RED),
AnsiFore.GREEN: (winterm.fore, WinColor.GREEN),
@@ -168,11 +209,21 @@ def write(self, text):
def reset_all(self):
+ global we_enabled_windows_vt
if self.convert:
self.call_win32('m', (0,))
elif not self.strip and not self.stream.closed:
self.wrapped.write(Style.RESET_ALL)
+ print("AnsiToWin32.reset_all() called")
+ if we_enabled_windows_vt:
+ stdout_handle = windll.kernel32.GetStdHandle(STDOUT)
+ # We enabled it, so turn it off again.
+ windll.kernel32.SetConsoleMode(stdout_handle, old_mode_value)
+ we_enabled_windows_vt = False
+ print("disabled virtual terminal on " + str(self.stream))
+ print("restored to " + str(old_mode_value))
+
def write_and_convert(self, text):
'''
diff --git a/colorama/tests/ansitowin32_test.py b/colorama/tests/ansitowin32_test.py
index 99ebd29..bbe99f4 100644
--- a/colorama/tests/ansitowin32_test.py
+++ b/colorama/tests/ansitowin32_test.py
@@ -170,13 +170,19 @@ def test_reset_all_shouldnt_raise_on_closed_orig_stdout(self):
def test_wrap_shouldnt_raise_on_closed_orig_stdout(self):
stream = StringIO()
stream.close()
- converter = AnsiToWin32(stream)
- self.assertFalse(converter.strip)
+ with \
+ patch("colorama.ansitowin32.os.name", "nt"), \
+ patch("colorama.ansitowin32.winapi_test", lambda: True):
+ converter = AnsiToWin32(stream)
+ self.assertTrue(converter.strip)
self.assertFalse(converter.convert)
def test_wrap_shouldnt_raise_on_missing_closed_attr(self):
- converter = AnsiToWin32(object())
- self.assertFalse(converter.strip)
+ with \
+ patch("colorama.ansitowin32.os.name", "nt"), \
+ patch("colorama.ansitowin32.winapi_test", lambda: True):
+ converter = AnsiToWin32(object())
+ self.assertTrue(converter.strip)
self.assertFalse(converter.convert)
def testExtractParams(self):
diff --git a/colorama/tests/undrev_test.py b/colorama/tests/undrev_test.py
new file mode 100644
index 0000000..4271da5
--- /dev/null
+++ b/colorama/tests/undrev_test.py
@@ -0,0 +1,50 @@
+import colorama
+# some ANSI escape sequence for colours and effects.
+BLACK = '\u001b[30m'
+RED = '\u001b[31m'
+GREEN = '\u001b[32m'
+YELLOW = '\u001b[33m'
+BLUE = '\u001b[34m'
+MAGENTA = '\u001b[35m'
+CYAN = '\u001b[36m'
+WHITE = '\u001b[37m'
+RESET = '\u001b[0m'
+
+BOLD = '\u001b[1m'
+UNDERLINE = '\u001b[4m'
+REVERSE = '\u001b[7m'
+
+
+print(RED, "this will be in red")
+print(" and this one also")
+
+print()
+
+def colour_print(text: str, effect: str) -> None:
+ """
+ Print 'text' using the ANSI sequence to change colour, etc
+ :param text: The text we want to print
+ :param effect: The effect we want. One of the constants
+ defined at the start of this module.
+ """
+ output_string = "{0}{1}{2}".format(effect, text, RESET)
+ print(output_string)
+
+
+# colorama.init()
+colour_print("Hello, red", RED)
+# Check that the colour was reset.
+print("This should be in the default terminal colour")
+colour_print("Hello, Blue", BLUE)
+colour_print("Hello, Yellow", YELLOW)
+colour_print("Hello, Bold", BOLD)
+colour_print("Hello, Underline", UNDERLINE)
+colour_print("Hello, Reverse", REVERSE)
+colour_print("Hello, Black", BLACK)
+
+print(colorama.Style.REVERSE_OFF + colorama.Back.GREEN + "off?" + colorama.Style.RESET_ALL)
+print(colorama.Style.REVERSE + colorama.Back.GREEN + "off?" + colorama.Style.RESET_ALL)
+
+print(colorama.Style.REVERSE + colorama.Style.REVERSE_OFF + colorama.Back.GREEN + "off?" + colorama.Style.RESET_ALL)
+
+# colorama.deinit()
diff --git a/colorama/win32.py b/colorama/win32.py
index c2d8360..5242ffe 100644
--- a/colorama/win32.py
+++ b/colorama/win32.py
@@ -3,6 +3,7 @@
# from winbase.h
STDOUT = -11
STDERR = -12
+ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004
try:
import ctypes
diff --git a/colorama/winterm.py b/colorama/winterm.py
index 0fdb4ec..a63503b 100644
--- a/colorama/winterm.py
+++ b/colorama/winterm.py
@@ -13,11 +13,18 @@ class WinColor(object):
YELLOW = 6
GREY = 7
+
# from wincon.h
class WinStyle(object):
- NORMAL = 0x00 # dim text, dim background
- BRIGHT = 0x08 # bright text, dim background
- BRIGHT_BACKGROUND = 0x80 # dim text, bright background
+ NORMAL = 0x00 # dim text, dim background
+ BRIGHT = 0x08 # bright text, dim background
+ BRIGHT_BACKGROUND = 0x80 # dim text, bright background
+
+ REVERSE = 0x4000
+ REVERSE_OFF = REVERSE
+ UNDERLINE = 0x8000
+ UNDERLINE_OFF = UNDERLINE
+
class WinTerm(object):
@@ -167,3 +174,6 @@ def erase_line(self, mode=0, on_stderr=False):
def set_title(self, title):
win32.SetConsoleTitle(title)
+
+ def not_implemented(self, *args, **kwargs):
+ pass
diff --git a/demos/demo01.py b/demos/demo01.py
index 99d896a..3f58b3d 100644
--- a/demos/demo01.py
+++ b/demos/demo01.py
@@ -18,7 +18,7 @@
# the foreground, background and style. The don't have any magic of their own.
FORES = [ Fore.BLACK, Fore.RED, Fore.GREEN, Fore.YELLOW, Fore.BLUE, Fore.MAGENTA, Fore.CYAN, Fore.WHITE ]
BACKS = [ Back.BLACK, Back.RED, Back.GREEN, Back.YELLOW, Back.BLUE, Back.MAGENTA, Back.CYAN, Back.WHITE ]
-STYLES = [ Style.DIM, Style.NORMAL, Style.BRIGHT ]
+STYLES = [ Style.DIM, Style.NORMAL, Style.BRIGHT, Style.UNDERLINE, Style.REVERSE ]
NAMES = {
Fore.BLACK: 'black', Fore.RED: 'red', Fore.GREEN: 'green', Fore.YELLOW: 'yellow', Fore.BLUE: 'blue', Fore.MAGENTA: 'magenta', Fore.CYAN: 'cyan', Fore.WHITE: 'white'
@@ -30,7 +30,7 @@
# show the color names
sys.stdout.write(' ')
for foreground in FORES:
- sys.stdout.write('%s%-7s' % (foreground, NAMES[foreground]))
+ sys.stdout.write('%s%-11s' % (foreground, NAMES[foreground]))
print()
# make a row for each background color
diff --git a/release.ps1 b/release.ps1
new file mode 100644
index 0000000..ac4e268
--- /dev/null
+++ b/release.ps1
@@ -0,0 +1,7 @@
+$ve="$HOME\.virtualenvs\colorama"
+$bin="$ve\Scripts"
+$version="$(& $bin\python.exe setup.py --version)"
+
+# Upload to PyPI.
+& $bin\twine.exe upload dist\colorama-$version*.tar.gz dist\colorama-$version-*.whl
+
diff --git a/screenshots/ubuntu-demo.png b/screenshots/ubuntu-demo.png
index 140a6ca..af70b2e 100644
Binary files a/screenshots/ubuntu-demo.png and b/screenshots/ubuntu-demo.png differ
diff --git a/screenshots/windows-demo.png b/screenshots/windows-demo.png
index efe5a60..485d348 100644
Binary files a/screenshots/windows-demo.png and b/screenshots/windows-demo.png differ
diff --git a/setup.py b/setup.py
index 31720d0..4e75c12 100644
--- a/setup.py
+++ b/setup.py
@@ -57,6 +57,7 @@ def get_version(path):
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
+ 'Programming Language :: Python :: 3.9',
'Programming Language :: Python :: Implementation :: CPython',
'Programming Language :: Python :: Implementation :: PyPy',
'Topic :: Terminals',
diff --git a/test-release b/test-release
new file mode 100644
index 0000000..15812ef
--- /dev/null
+++ b/test-release
@@ -0,0 +1,43 @@
+#!/usr/bin/env bash
+
+# Test the currently built release of Colorama from the dist/ dir.
+# Run this before making a release.
+#
+# This should be run on Windows, because Colorama is mostly a no-op elsewhere.
+# Hmmm, this script should probably be a .bat file then? Nah, WSL FTW.
+#
+# Uploads package from the dist/ directory to the *test* PyPI.
+# Create a fresh virtualenvironment and install colorama from test PyPI.
+# Import Colorama and make trivial use of it.
+
+# Exit on error
+set -eu -o pipefail
+
+syspython=python3.8
+bin="$HOME/.virtualenvs/colorama/bin"
+version=$($bin/python setup.py --version)
+sandbox=test-release-playground
+
+# Upload to the test PyPI.
+$bin/twine upload --repository testpypi dist/colorama-$version-* \
+ || echo " > Expect a 400 if package was already uploaded."
+
+# cd elsewhere so we cannot import from local source.
+mkdir -p $sandbox
+(
+ cd $sandbox
+
+ # Create a temporary disposable virtualenv.
+ $syspython -m venv --clear venv
+
+ # Install the package we just uploaded.
+ # (--extra-index-url for this project's requirements)
+ venv/bin/python -m pip --quiet install --index-url https://test.pypi.org/simple --extra-index-url https://pypi.org/simple colorama==$version
+
+ # Import and use Colorama from the temp virtualenv.
+ venv/bin/python -c "import colorama; colorama.init(); print(colorama.Fore.GREEN + \"OK: Colorama\", colorama.__version__, \"from test pypi install.\")"
+)
+
+# Tidy up
+rm -rf $sandbox
+
diff --git a/test-release.ps1 b/test-release.ps1
new file mode 100644
index 0000000..19c6967
--- /dev/null
+++ b/test-release.ps1
@@ -0,0 +1,30 @@
+$syspython="python3.8.exe"
+$ve="$HOME\.virtualenvs\colorama"
+$bin="$ve\Scripts"
+$version="$(& $bin\python.exe setup.py --version)"
+
+# Upload to the test PyPI.
+& $bin\twine.exe upload --repository testpypi dist\colorama-$version-*
+if(!$?) {
+ write-host " > Expect a 400 if package was already uploaded"
+}
+
+# cd elsewhere so we cannot import from local source.
+mkdir -force sandbox | out-null
+cd sandbox
+
+# Create a temporary disposable virtualenv.
+& $syspython -m venv --clear venv
+
+# Install the package we just uploaded.
+# (--extra-index-url for this project's requirements)
+venv\Scripts\python -m pip --quiet install --index-url https://test.pypi.org/simple --extra-index-url https://pypi.org/simple colorama==$version
+# Import and use colorama from the temp virtualenv.
+venv\Scripts\python.exe -c @"
+import colorama;
+colorama.init();
+print(colorama.Fore.GREEN + ""OK Colorama "" + colorama.__version__ + "" from test pypi install."")
+"@
+
+cd ..
+
diff --git a/test.ps1 b/test.ps1
new file mode 100644
index 0000000..03ea144
--- /dev/null
+++ b/test.ps1
@@ -0,0 +1,5 @@
+$ve="$HOME\.virtualenvs\colorama"
+$bin="$ve\Scripts"
+
+& $bin\python.exe -m unittest discover -p *_test.py
+
diff --git a/tox.ini b/tox.ini
index aa8c783..1588d99 100644
--- a/tox.ini
+++ b/tox.ini
@@ -1,5 +1,5 @@
[tox]
-envlist = py27, py35, py36, py37, py38, pypy
+envlist = py27, py35, py36, py37, py38, py39, pypy
[testenv]
deps = py27,pypy: mock