├── .github ├── FUNDING.yml └── workflows │ ├── lint_python.yml │ └── test.yml ├── .gitignore ├── CHANGELOG.rst ├── ENTERPRISE.md ├── LICENSE.txt ├── Makefile ├── README-hacking.md ├── README.rst ├── SECURITY.md ├── bootstrap.ps1 ├── build.ps1 ├── clean.ps1 ├── colorama ├── __init__.py ├── ansi.py ├── ansitowin32.py ├── initialise.py ├── tests │ ├── __init__.py │ ├── ansi_test.py │ ├── ansitowin32_test.py │ ├── initialise_test.py │ ├── isatty_test.py │ ├── utils.py │ └── winterm_test.py ├── win32.py └── winterm.py ├── demos ├── demo.bat ├── demo.sh ├── demo01.py ├── demo02.py ├── demo03.py ├── demo04.py ├── demo05.py ├── demo06.py ├── demo07.py ├── demo08.py ├── demo09.py └── fixpath.py ├── pyproject.toml ├── release.ps1 ├── requirements-dev.txt ├── requirements.txt ├── screenshots ├── ubuntu-demo.png └── windows-demo.png ├── test-release ├── test-release.ps1 ├── test.ps1 └── tox.ini /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | # community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 4 | # custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 5 | # github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 6 | # issuehunt: # Replace with a single IssueHunt username 7 | # ko_fi: # Replace with a single Ko-fi username 8 | # liberapay: # Replace with a single Liberapay username 9 | # open_collective: # Replace with a single Open Collective username 10 | # otechie: # Replace with a single Otechie username 11 | # patreon: # Replace with a single Patreon username 12 | custom: ["https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=2MZ9D2GMLYCUJ&item_name=Colorama¤cy_code=USD"] 13 | tidelift: "pypi/colorama" 14 | 15 | -------------------------------------------------------------------------------- /.github/workflows/lint_python.yml: -------------------------------------------------------------------------------- 1 | name: lint_python 2 | on: [pull_request, push] 3 | jobs: 4 | lint_python: 5 | runs-on: ubuntu-latest 6 | steps: 7 | - uses: actions/checkout@v2 8 | - uses: actions/setup-python@v2 9 | - run: pip install --upgrade pip wheel 10 | - run: pip install bandit black codespell flake8 flake8-bugbear 11 | flake8-comprehensions isort mypy pytest pyupgrade safety 12 | - run: bandit --recursive --skip B311 . 13 | - run: black --check . || true 14 | - run: codespell # --ignore-words-list="" --skip="*.css,*.js,*.lock" 15 | - run: flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics 16 | - run: flake8 . --count --exit-zero --max-complexity=10 --max-line-length=88 17 | --show-source --statistics 18 | - run: isort --check-only --profile black . || true 19 | - run: pip install -r requirements.txt 20 | - run: mkdir --parents --verbose .mypy_cache 21 | - run: mypy --ignore-missing-imports --install-types --non-interactive . || true 22 | - run: pytest . 23 | - run: shopt -s globstar && pyupgrade --py36-plus **/*.py || true 24 | - run: safety check 25 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: [push, pull_request, workflow_dispatch] 4 | 5 | env: 6 | FORCE_COLOR: 1 7 | 8 | jobs: 9 | test: 10 | runs-on: ${{ matrix.os }} 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | python-version: ["pypy-2.7", "pypy-3.8", "2.7", "3.7", "3.8", "3.9", "3.10", "3.11", "3.12"] 15 | os: [ubuntu-latest, macos-latest, windows-latest] 16 | include: 17 | # Add new helper variables to existing jobs 18 | - {python-version: "pypy-2.7", toxenv: "pypy"} 19 | - {python-version: "pypy-3.8", toxenv: "pypy3"} 20 | - {python-version: "2.7", toxenv: "py27"} 21 | - {python-version: "3.7", toxenv: "py37"} 22 | - {python-version: "3.8", toxenv: "py38"} 23 | - {python-version: "3.9", toxenv: "py39"} 24 | - {python-version: "3.10", toxenv: "py310"} 25 | - {python-version: "3.11", toxenv: "py311"} 26 | - {python-version: "3.12", toxenv: "py312"} 27 | 28 | steps: 29 | - uses: actions/checkout@v3 30 | 31 | - name: Set up Python ${{ matrix.python-version }} 32 | uses: actions/setup-python@v4 33 | with: 34 | python-version: ${{ matrix.python-version }} 35 | allow-prereleases: true 36 | 37 | - name: Get pip cache dir 38 | id: pip-cache 39 | run: | 40 | echo "::set-output name=dir::$(pip cache dir)" 41 | 42 | - name: Cache 43 | uses: actions/cache@v3 44 | with: 45 | path: ${{ steps.pip-cache.outputs.dir }} 46 | key: 47 | ${{ matrix.os }}-${{ matrix.python-version }}-v1-${{ hashFiles('**/tox.ini') }} 48 | restore-keys: | 49 | ${{ matrix.os }}-${{ matrix.python-version }}-v1- 50 | 51 | - name: Install dependencies 52 | run: | 53 | python -m pip install tox 54 | 55 | - name: Tox tests 56 | run: | 57 | tox -e ${{ matrix.toxenv }} 58 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | *.egg-info 3 | .coverage 4 | .tox/ 5 | /MANIFEST 6 | /build/ 7 | /dist/ 8 | /sandbox/ 9 | /tags 10 | virtualenv 11 | 12 | # PyCharm 13 | .idea 14 | -------------------------------------------------------------------------------- /CHANGELOG.rst: -------------------------------------------------------------------------------- 1 | 0.4.6 Current release 2 | * https://github.com/tartley/colorama/pull/139 Add alternative to 'init()', 3 | called 'just_fix_windows_console'. This fixes many longstanding problems 4 | with 'init', such as working incorrectly on modern Windows terminals, and 5 | wonkiness when init gets called multiple times. The intention is that it 6 | just makes all Windows terminals treat ANSI the same way as other terminals 7 | do. Many thanks the njsmith for fixing our messes. 8 | * https://github.com/tartley/colorama/pull/352 Support Windows 10's ANSI/VT 9 | console. This didn't exist when Colorama was created, and avoiding us 10 | causing havok there is long overdue. Thanks to segeviner for the initial 11 | approach, and to njsmith for getting it merged. 12 | * https://github.com/tartley/colorama/pull/338 Internal overhaul of package 13 | metadata declaration, which abolishes our use of the now heavily 14 | discouraged setuptools (and hence setup.py, setup.cfg and MANIFEST.in), in 15 | favor of hatchling (and hence pyproject.toml), generously contributed by 16 | ofek (author of hatchling). This includes dropping support Python3.5 and 17 | 3.6, which are EOL, and were already dropped from setuptools, so this 18 | should not affect our users. 19 | * https://github.com/tartley/colorama/pull/353 Attention to detail award to 20 | LqdBcnAtWork for a spelling fix in demo06 21 | 0.4.5 22 | * Catch a racy ValueError that could occur on exit. 23 | * Create README-hacking.md, for Colorama contributors. 24 | * Tweak some README unicode characters that don't render correctly on PyPI. 25 | * Fix some tests that were failing on some operating systems. 26 | * Add support for Python 3.9. 27 | * Add support for PyPy3. 28 | * Add support for pickling with the ``dill`` module. 29 | 0.4.4 30 | * Re-org of README, to put the most insteresting parts near the top. 31 | * Added Linux makefile targets and Windows powershell scripts to automate 32 | bootstrapping a development environment, and automate the process of 33 | testing wheels before they are uploaded to PyPI. 34 | * Use stdlib unittest.mock where available 35 | * Travis CI now also builds on arm64 36 | * Demo06 demonstrates existing cursor positioning feature 37 | * Fix OSC regex & handling to prevent hang or crash 38 | * Document enterprise support by Tidelift 39 | 0.4.3 40 | * Fix release 0.4.2 which was uploaded with missing files. 41 | 0.4.2 BROKEN DO NOT USE 42 | * #228: Drop support for EOL Python 3.4, and add 3.7 and 3.8. Thanks to 43 | hugovk. 44 | * Several additions and fixes to documentation and metadata. 45 | * Added Tidelift subscription information. 46 | 0.4.1 47 | * Fix issue #196: prevent exponential number of calls when calling 'init' 48 | multiple times. Reported by bbayles and fixed by Delgan. 49 | 0.4.0 50 | * Fix issue #142: reset LIGHT_EX colors with RESET_ALL. Reported by Delgan 51 | * Fix issue #147: ignore invalid "erase" ANSI codes. Reported by shin- 52 | * Fix issues #163 and #164: fix stream wrapping under PyCharm. Contributed by 53 | veleek and Delgan. 54 | * Thanks to jdufresne for various code cleanup and updates to documentation 55 | and project metadata. (pull requests #171, #172, #173, #174, #176, #177, 56 | #189, #190, #192) 57 | * #186: added contextlib magic methods to ansitowin32.StreamWrapper. 58 | Contributed by hoefling. 59 | * Fix issue #131: don't cache stdio handles, since they might be 60 | closed/changed by fd redirection. This fixes an issue with pytest. 61 | Contributed by segevfiner. 62 | * #146, #157: Drop support for EOL Python 2.5, 2.6, 3.1, 3.2 and 3.3, and add 63 | 3.6. Thanks to hugovk. 64 | 0.3.9 65 | * Revert fix for issue #103 which causes problems for dependent applications 66 | 0.3.8 67 | * Fix issue #121: "invalid escape sequence" deprecation fixes on Python 3.6+ 68 | * Fix issue #110: fix "set console title" when working with unicode strings 69 | * Fix issue #103: enable color when using "input" function on Python 3.5+ 70 | * Fix issue #95: enable color when stderr is a tty but stdout is not 71 | 0.3.7 72 | * Fix issue #84: check if stream has 'closed' attribute before testing it 73 | * Fix issue #74: objects might become None at exit 74 | 0.3.6 75 | * Fix issue #81: fix ValueError when a closed stream was used 76 | 0.3.5 77 | * Bumping version to re-upload a wheel distribution 78 | 0.3.4 79 | * Fix issue #47 and #80 - stream redirection now strips ANSI codes on Linux 80 | * Fix issue #53 - strip readline markers 81 | * Fix issue #32 - assign orig_stdout and orig_stderr when initialising 82 | * Fix issue #57 - Fore.RESET did not reset style of LIGHT_EX colors. Fixed by 83 | Andy Neff 84 | * Fix issue #51 - add context manager syntax. Thanks to Matt Olsen. 85 | * Fix issue #48 - colorama didn't work on Windows when environment variable 86 | 'TERM' was set. 87 | * Fix issue #54 - fix pylint errors in client code. 88 | * Changes to readme and other improvements by Marc Abramowitz and Zearin 89 | 0.3.3 90 | * Fix Google Code issue #13 - support changing the console title with OSC 91 | escape sequence 92 | * Fix Google Code issue #16 - Add support for Windows xterm emulators 93 | * Fix Google Code issue #30 - implement \033[nK (clear line) 94 | * Fix Google Code issue #49 - no need to adjust for scroll when new position 95 | is already relative (CSI n A\B\C\D) 96 | * Fix Google Code issue #55 - erase_data fails on Python 3.x 97 | * Fix Google Code issue #46 - win32.COORD definition missing 98 | * Implement \033[0J and \033[1J (clear screen options) 99 | * Fix default ANSI parameters 100 | * Fix position after \033[2J (clear screen) 101 | * Add command shortcuts: colorama.Cursor, colorama.ansi.set_title, 102 | colorama.ansi.clear_line, colorama.ansi.clear_screen 103 | * Fix issue #22 - Importing fails for python3 on Windows 104 | * Thanks to John Szakmeister for adding support for light colors 105 | * Thanks to Charles Merriam for adding documentation to demos 106 | 0.3.2 107 | * Thanks to Marc Schlaich (schlamar) for a setup.py fix for Python2.5 108 | * Thanks to Jurko for fix on 64-bit Windows CPython2.5 w/o ctypes (Google 109 | Code issue #56) 110 | * Thanks to Remi Rampin for: 111 | * better github integration, incl rendered README and Travis config. 112 | * fixed forward slashes in README 113 | * Thanks to Florian Bruhin for fix when stdout or stderr are None 114 | * Thanks to Simeon Visser for: 115 | * closing a file handle using 'with' 116 | * updating classifiers to include Python 3.3 and 3.4 117 | * Thanks to Thomas Weininger for fix ValueError on Windows (Google Code issue 118 | #50) 119 | 0.3.1 120 | * Fixed crash on exit with closed stdout, with thanks to Marc Abramowitz. 121 | * Now uses setuptools if available, and falls back to distutils if not. 122 | * setup.py no longer imports anything from colorama source. 123 | 0.3.0 124 | * Move repository to Git, https://github.com/tartley/colorama. (My Mercurial 125 | repo seemed to be corrupted, I couldn't commit nor view patches of old 126 | commits, even on fresh checkouts.) 127 | * Fix always-crash on non-Windows platforms, reported by Matt McCormick. 128 | * Fix Google Code issue #47, incompatible with pyreadline. 129 | 0.2.7 130 | * Fix problem under 64-bit windows due to ctypes HANDLE size. Submitted by 131 | the rather magnificent Ben Hoyt. This fixes Google Code issue #43 132 | 0.2.6 133 | * Add copyright & licensing info to every file, as requested by a large 134 | downstream project which has problems making sure that all 3rd party 135 | contributions have appropriate license. 136 | 0.2.5 137 | * Several documentation & demo fixes. 138 | 0.2.4 139 | * Fix to work on Windows 7. 140 | * Python 3 compatibility in docs and demos. 141 | * Add handling for 'cursor up' and 'get position' ANSI codes. 142 | 0.2.3 143 | * Split changelog out into separate file. 144 | 0.2.2 145 | * Fix bug which caused init() to raise, introduced in 0.2.1. 146 | * Remove asserts which cause problems in various circumstances. At least some 147 | users saw asserts fail on 'success' returned from win32 functions, even 148 | though the win32 functions appear to have worked correctly. 149 | 0.2.1 150 | * Completely broken: I added a bug which caused init() to raise. 151 | * Added some documentation for cursor positioning and clear screen to README. 152 | * Add 'reinit' and 'deinit' functions, as suggested by Charles FOL and 153 | Romanov DA. 154 | 0.2 155 | * Merge in changes from Daniel Griffith: Add ANSI cursor positioning & 156 | partial support for clear screen. Patch submitted by Oscar Lester, don't 157 | send RESET_ALL to non-tty. 158 | * Demos split into separate files and moved into their own directory. 159 | * Tweak sys.path in demos so they run against local source, not installed 160 | version of Colorama. 161 | 0.1.18 162 | * Fix README (no such attr as Fore.DEFAULT, etc), kindly reported by nodakai. 163 | 0.1.17 164 | * Prevent printing of garbage ANSI codes upon installing with pip 165 | 0.1.16 166 | * Re-upload to fix previous error. Make clean now removes old MANIFEST. 167 | 0.1.15 168 | * Completely broken. Distribution was empty due to leftover invalid MANIFEST 169 | file from building on a different platform. 170 | * Fix python3 incompatibility kindly reported by G |uumlaut| nter Kolousek 171 | 0.1.14 172 | * Fix hard-coded reset to white-on-black colors. Fore.RESET, Back.RESET and 173 | Style.RESET_ALL now revert to the colors as they were when init() was 174 | called. Some lessons hopefully learned about testing prior to release. 175 | 0.1.13 176 | * Completely broken: barfed when installed using pip. 177 | 0.1.12 178 | * Completely broken: contained no source code. double oops. 179 | 0.1.11 180 | * Completely broken: fatal import errors on Ubuntu. oops. 181 | 0.1.10 182 | * Stop emulating 'bright' text with bright backgrounds. 183 | * Display 'normal' text using win32 normal foreground instead of bright. 184 | * Drop support for 'dim' text. 185 | 0.1.9 186 | * Fix incompatibility with Python 2.5 and earlier. 187 | * Remove setup.py dependency on setuptools, now uses stdlib distutils. 188 | 0.1.8 189 | * Fix ghastly errors all over the place on Ubuntu. 190 | * Add init kwargs 'convert' and 'strip', which supersede the old 'wrap'. 191 | 0.1.7 192 | * Python 3 compatible. 193 | * Fix: Now strips ansi on windows without necessarily converting it to win32 194 | calls (eg. if output is not a tty.) 195 | * Fix: Flaky interaction of interleaved ansi sent to stdout and stderr. 196 | * Improved demo.sh (hg checkout only.) 197 | 0.1.6 198 | * Fix ansi sequences with no params now default to parmlist of [0]. 199 | * Fix flaky behaviour of autoreset and reset_all atexit. 200 | * Fix stacking of repeated atexit calls - now just called once. 201 | * Fix ghastly import problems while running tests. 202 | * 'demo.py' (hg checkout only) now demonstrates autoreset and reset atexit. 203 | * Provide colorama.VERSION, used by setup.py. 204 | * Tests defanged so they no longer actually change terminal color when run. 205 | 0.1.5 206 | * Now works on Ubuntu. 207 | 0.1.4 208 | * Implemented RESET_ALL on application exit 209 | 0.1.3 210 | * Implemented init(wrap=False) 211 | 0.1.2 212 | * Implemented init(autoreset=True) 213 | 0.1.1 214 | * Minor tidy 215 | 0.1 216 | * Works on Windows for foreground color, background color, bright or dim 217 | 218 | 219 | .. |uumlaut| unicode:: U+00FC .. u with umlaut 220 | :trim: 221 | -------------------------------------------------------------------------------- /ENTERPRISE.md: -------------------------------------------------------------------------------- 1 | # Colorama for enterprise. 2 | 3 | *Available as part of the Tidelift Subscription.* 4 | 5 | Tidelift is working with the maintainers of Colorama and thousands of other 6 | open source projects to deliver commercial support and maintenance for the open 7 | source dependencies you use to build your applications. Save time, reduce risk, 8 | and improve code health, while paying the maintainers of the exact dependencies 9 | you use. 10 | 11 | [\[Learn More\]](https://tidelift.com/subscription/pkg/pypi-colorama?utm_source=pypi-colorama&utm_medium=referral&utm_campaign=enterprise) [\[Request a demo\]](https://tidelift.com/subscription/request-a-demo?utm_source=pypi-colorama&utm_medium=referral&utm_campaign=enterprise) 12 | 13 | ### Enterprise-ready open source software—managed for you 14 | 15 | The Tidelift Subscription is a managed open source subscription for application dependencies covering millions of open source projects across JavaScript, Python, Java, PHP, Ruby, .NET, and more. 16 | 17 | Your subscription includes: 18 | 19 | **Security updates:** 20 | Tidelift’s security response team coordinates patches for new breaking security 21 | vulnerabilities and alerts immediately through a private channel, so your 22 | software supply chain is always secure. 23 | 24 | **Licensing verification and indemnification:** 25 | Tidelift verifies license information to enable easy policy enforcement and 26 | adds intellectual property indemnification to cover creators and users in case 27 | something goes wrong. You always have a 100% up-to-date bill of materials for 28 | your dependencies to share with your legal team, customers, or partners. 29 | 30 | **Maintenance and code improvement:** 31 | Tidelift ensures the software you rely on keeps working as long as you need it 32 | to work. Your managed dependencies are actively maintained and we recruit 33 | additional maintainers where required. 34 | 35 | **Package selection and version guidance:** 36 | We help you choose the best open source packages from the start—and then guide 37 | you through updates to stay on the best releases as new issues arise. 38 | 39 | **Roadmap input:** 40 | Take a seat at the table with the creators behind the software you use. 41 | Tidelift’s participating maintainers earn more income as their software is used 42 | by more subscribers, so they’re interested in knowing what you need. 43 | 44 | **Tooling and cloud integration:** 45 | Tidelift works with GitHub, GitLab, BitBucket, and more. We support every cloud 46 | platform (and other deployment targets, too). 47 | 48 | The end result? All of the capabilities you expect from commercial-grade software, for the full breadth of open source you use. That means less time grappling with esoteric open source trivia, and more time building your own applications—and your business. 49 | 50 | [\[Learn More\]](https://tidelift.com/subscription/pkg/pypi-colorama?utm_source=pypi-colorama&utm_medium=referral&utm_campaign=enterprise) [\[Request a demo\]](https://tidelift.com/subscription/request-a-demo?utm_source=pypi-colorama&utm_medium=referral&utm_campaign=enterprise) 51 | 52 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 Jonathan Hartley 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | * Neither the name of the copyright holders, nor those of its contributors 15 | may be used to endorse or promote products derived from this software without 16 | specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 19 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 22 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 24 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 25 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 26 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE.txt file. 2 | 3 | # This makefile is just a cheatsheet to remind me of some commonly used 4 | # commands. I generally am executing these commands on Ubuntu, or on WindowsXP 5 | # with Cygwin binaries at the start of the PATH. 6 | 7 | NAME=colorama 8 | 9 | help: ## Display help for documented make targets. 10 | @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | \ 11 | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-7s\033[0m %s\n", $$1, $$2}' 12 | 13 | 14 | # bootstrap environment 15 | 16 | virtualenv=~/.virtualenvs/colorama 17 | pip=$(virtualenv)/bin/pip 18 | syspython=python3 19 | python=$(virtualenv)/bin/python 20 | twine=$(virtualenv)/bin/twine 21 | 22 | clean: ## Remove build artifacts, .pyc files, virtualenv 23 | -rm -rf build dist MANIFEST colorama.egg-info $(virtualenv) 24 | -find . -type f -name '*.py[co]' -delete -o -type d -name __pycache__ -delete 25 | .PHONY: clean 26 | 27 | $(virtualenv): 28 | $(syspython) -m venv --clear $(virtualenv) 29 | $(pip) install --upgrade pip 30 | 31 | venv: $(virtualenv) ## Create or clear a virtualenv 32 | .PHONY: venv 33 | 34 | bootstrap: venv ## Populate the virtualenv 35 | $(pip) install -r requirements.txt -r requirements-dev.txt 36 | .PHONY: bootstrap 37 | 38 | 39 | # development 40 | 41 | tags: ## Create tags file 42 | ctags -R ${NAME} 43 | .PHONY: tags 44 | 45 | test: ## Run tests 46 | $(python) -m unittest discover -p *_test.py 47 | .PHONY: test 48 | 49 | 50 | # build packages 51 | 52 | build: ## Build a release (sdist and wheel) 53 | $(python) -m build 54 | .PHONY: build 55 | 56 | test-release: build ## Test a built release 57 | ./test-release 58 | .PHONY: test-release 59 | 60 | release: ## Upload a built release 61 | $(twine) upload --repository=colorama dist/colorama-* 62 | .PHONY: release 63 | -------------------------------------------------------------------------------- /README-hacking.md: -------------------------------------------------------------------------------- 1 | # Colorama Development 2 | 3 | Help and fixes are welcome! 4 | 5 | Although Colorama has no requirements other than the Python standard library, 6 | development requires some Python packages, which are captured in 7 | requirements-dev.txt. 8 | 9 | Throughout, if you're on a Mac, you can probably do something similar to the 10 | Linux instructions. Either use the makefile directly, or look in it to see 11 | what commands it executes, and manually execute something similar. PRs to 12 | automate for Mac appreciated! Especially if they just made the existing Linux 13 | Makefile targets work on Mac too. 14 | 15 | ## Desired changes 16 | 17 | Colorama is unexpectedly popular, and is now a transitive dependency of many 18 | popular and high profile projects. If we break backwards compatibility, even in a 19 | subtle way, we can break applications - or pip installs - for lots of people. 20 | 21 | In addition, the project already takes more time & energy to maintain than 22 | the maintainers currently have available - for example the original author 23 | is now a parent, and no longer uses Windows, so time and motivation for this 24 | project are both much lower than they used to be. 25 | 26 | As a result of both the above, we are very conservative in what sorts of 27 | changes we can accept. Generally, we are not keen on new features. Even if 28 | they are small, they still add to the future maintenance burden, increasing 29 | the surface area into which future bugs or compatibility breaks could be 30 | introduced. 31 | 32 | This is especially true if they are new ways to generate ANSI codes (e.g. 33 | context managers for handling Fore, Back or Style changes.), since it has 34 | always been Colorama's stance that if you want to print ANSI codes, then yes 35 | we can help out with that in a rudimentary way, but if you want to do advanced 36 | things, then you should be using a different library that specializes in that, 37 | such as Termcolor, Blessings, or Rich. These libraries are much better than 38 | Colorama at generating ANSI codes for colors and the like, and probably 39 | already include the feature you are trying to add to Colorama, plus many 40 | more. 41 | 42 | In addition to using those libraries, if you call colorama.init(), then your 43 | fancy new colors, etc, will also work on Windows. This is the main purpose 44 | of Colorama. 45 | 46 | The kinds of submissions we would encourage work towards that goal, or fix 47 | bugs, or improve compatibility across operating systems or environments. 48 | 49 | ## Makefile and PowerShell scripts 50 | 51 | Some common commands are captured as Linux makefile targets (which could 52 | perhaps be coaxed into running on OSX in Bash), and as Windows PowerShell 53 | scripts. 54 | 55 | | Task | Linux | Windows | 56 | |---------------------------------|---------------------|----------------------| 57 | | Create & populate virtualenv. | `make bootstrap` | `.\bootstrap.ps1` | 58 | | Run tests. | `make test` | `.\test.ps1` | 59 | | Build a wheel. | `make build` | `.\build.ps1` | 60 | | Test the wheel. | `make test-release` | `.\test-release.ps1` | 61 | | Release the wheel on PyPI | `make release` | `.\release.ps1` | 62 | | Clean generated files & builds. | `make clean` | `.\clean.ps1` | 63 | 64 | The Makefile is self-documenting, so 'make' with no args will describe each 65 | target. 66 | 67 | ## Release checklist 68 | 69 | 1. Check the CHANGELOG.rst is updated with everything since the last release, 70 | including links to merged PRs. Move the "Current release" comment from the 71 | previous version number. 72 | 73 | 2. First we'll make a candidate release. Ensure the '.rc1' suffix is 74 | present on `__version__` in `colorama/__init.py__.py`, eg: 75 | 76 | __version__ = '0.4.6rc1' 77 | 78 | 3. Run the tests locally on your preferred OS, just to save you from doing 79 | the subsequent time-consuming steps while there are still obvious problems 80 | in the code: 81 | 82 | * Windows: 83 | * First allow powershell to execute scripts, see: 84 | https://stackoverflow.com/a/32328091 85 | * `powershell bootstrap.ps1` 86 | * `powershell test.ps1` 87 | * Linux: 88 | * `make bootstrap` 89 | * `make test` 90 | 91 | 4. Verify you're all committed, merged to master. 92 | 93 | 5. Tag the current commit with the `__version__` from `colorama/__init__.py`. 94 | We should start using 95 | [annotated tags for releases](https://www.tartley.com/posts/git-annotated-tags), so: 96 | 97 | git tag -a -m "" $version 98 | git push --follow-tags 99 | 100 | 6. Build the distributables (sdist and wheel), on either OS: 101 | 102 | * Windows: `.\build.ps1` 103 | * Linux: `make build` 104 | 105 | 7. Test the distributables on both OS. Whichever one you do 2nd will get an 106 | HTTP 400 response on uploading to test.pypi.org, but outputs a message 107 | saying this is expected and carries on: 108 | 109 | * Windows: `.\test-release.ps1` 110 | * Linux: `make test-release` 111 | 112 | (This currently only tests the wheel, but 113 | [should soon test the sdist too](https://github.com/tartley/colorama/issues/286).) 114 | 115 | 8. Check the [CI builds](https://github.com/tartley/colorama/actions/) 116 | are complete and all passing. 117 | 118 | 9. Upload the distributables to PyPI: 119 | 120 | * On Windows: `.\release.ps1` 121 | * On Linux: `make release` 122 | 123 | 10. Test by installing the candidate version from PyPI, and sanity check it with 124 | 'demo.sh', making sure this is running against the PyPI installation, not 125 | local source. 126 | 127 | 11. Maybe wait a day for anyone using pre-release installs to report any 128 | problems? 129 | 130 | 12. Remove the '.rcX' suffix from `__version__` in 131 | `colorama/__init__.py`. 132 | 133 | 13. Repeat steps 4 to 9, for the actual (non-candidate) release. 134 | 135 | 14. Bump the version number in `colorama/__init__.py`, and add a 'dev1' 136 | suffix, eg: 137 | 138 | `0.4.5dev1` 139 | 140 | so that any build artifacts created are clearly labelled as not a real 141 | release. Commit and push this (directly to master is fine.) 142 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | .. image:: https://img.shields.io/pypi/v/colorama.svg 2 | :target: https://pypi.org/project/colorama/ 3 | :alt: Latest Version 4 | 5 | .. image:: https://img.shields.io/pypi/pyversions/colorama.svg 6 | :target: https://pypi.org/project/colorama/ 7 | :alt: Supported Python versions 8 | 9 | .. image:: https://github.com/tartley/colorama/actions/workflows/test.yml/badge.svg 10 | :target: https://github.com/tartley/colorama/actions/workflows/test.yml 11 | :alt: Build Status 12 | 13 | Colorama 14 | ======== 15 | 16 | Makes ANSI escape character sequences (for producing colored terminal text and 17 | cursor positioning) work under MS Windows. 18 | 19 | .. |donate| image:: https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif 20 | :target: https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=2MZ9D2GMLYCUJ&item_name=Colorama¤cy_code=USD 21 | :alt: Donate with Paypal 22 | 23 | `PyPI for releases `_ | 24 | `Github for source `_ | 25 | `Colorama for enterprise on Tidelift `_ 26 | 27 | If you find Colorama useful, please |donate| to the authors. Thank you! 28 | 29 | Installation 30 | ------------ 31 | 32 | Tested on CPython 2.7, 3.7, 3.8, 3.9, 3.10, 3.11 and 3.12 and PyPy 2.7 and 3.8. 33 | 34 | No requirements other than the standard library. 35 | 36 | .. code-block:: bash 37 | 38 | pip install colorama 39 | # or 40 | conda install -c anaconda colorama 41 | 42 | Description 43 | ----------- 44 | 45 | ANSI escape character sequences have long been used to produce colored terminal 46 | text and cursor positioning on Unix and Macs. Colorama makes this work on 47 | Windows, too, by wrapping ``stdout``, stripping ANSI sequences it finds (which 48 | would appear as gobbledygook in the output), and converting them into the 49 | appropriate win32 calls to modify the state of the terminal. On other platforms, 50 | Colorama does nothing. 51 | 52 | This has the upshot of providing a simple cross-platform API for printing 53 | colored terminal text from Python, and has the happy side-effect that existing 54 | applications or libraries which use ANSI sequences to produce colored output on 55 | Linux or Macs can now also work on Windows, simply by calling 56 | ``colorama.just_fix_windows_console()`` (since v0.4.6) or ``colorama.init()`` 57 | (all versions, but may have other side-effects – see below). 58 | 59 | An alternative approach is to install ``ansi.sys`` on Windows machines, which 60 | provides the same behaviour for all applications running in terminals. Colorama 61 | is intended for situations where that isn't easy (e.g., maybe your app doesn't 62 | have an installer.) 63 | 64 | Demo scripts in the source code repository print some colored text using 65 | ANSI sequences. Compare their output under Gnome-terminal's built in ANSI 66 | handling, versus on Windows Command-Prompt using Colorama: 67 | 68 | .. image:: https://github.com/tartley/colorama/raw/master/screenshots/ubuntu-demo.png 69 | :width: 661 70 | :height: 357 71 | :alt: ANSI sequences on Ubuntu under gnome-terminal. 72 | 73 | .. image:: https://github.com/tartley/colorama/raw/master/screenshots/windows-demo.png 74 | :width: 668 75 | :height: 325 76 | :alt: Same ANSI sequences on Windows, using Colorama. 77 | 78 | These screenshots show that, on Windows, Colorama does not support ANSI 'dim 79 | text'; it looks the same as 'normal text'. 80 | 81 | Usage 82 | ----- 83 | 84 | Initialisation 85 | .............. 86 | 87 | If the only thing you want from Colorama is to get ANSI escapes to work on 88 | Windows, then run: 89 | 90 | .. code-block:: python 91 | 92 | from colorama import just_fix_windows_console 93 | just_fix_windows_console() 94 | 95 | If you're on a recent version of Windows 10 or better, and your stdout/stderr 96 | are pointing to a Windows console, then this will flip the magic configuration 97 | switch to enable Windows' built-in ANSI support. 98 | 99 | If you're on an older version of Windows, and your stdout/stderr are pointing to 100 | a Windows console, then this will wrap ``sys.stdout`` and/or ``sys.stderr`` in a 101 | magic file object that intercepts ANSI escape sequences and issues the 102 | appropriate Win32 calls to emulate them. 103 | 104 | In all other circumstances, it does nothing whatsoever. Basically the idea is 105 | that this makes Windows act like Unix with respect to ANSI escape handling. 106 | 107 | It's safe to call this function multiple times. It's safe to call this function 108 | on non-Windows platforms, but it won't do anything. It's safe to call this 109 | function when one or both of your stdout/stderr are redirected to a file – it 110 | won't do anything to those streams. 111 | 112 | Alternatively, you can use the older interface with more features (but also more 113 | potential footguns): 114 | 115 | .. code-block:: python 116 | 117 | from colorama import init 118 | init() 119 | 120 | This does the same thing as ``just_fix_windows_console``, except for the 121 | following differences: 122 | 123 | - It's not safe to call ``init`` multiple times; you can end up with multiple 124 | layers of wrapping and broken ANSI support. 125 | 126 | - Colorama will apply a heuristic to guess whether stdout/stderr support ANSI, 127 | and if it thinks they don't, then it will wrap ``sys.stdout`` and 128 | ``sys.stderr`` in a magic file object that strips out ANSI escape sequences 129 | before printing them. This happens on all platforms, and can be convenient if 130 | you want to write your code to emit ANSI escape sequences unconditionally, and 131 | let Colorama decide whether they should actually be output. But note that 132 | Colorama's heuristic is not particularly clever. 133 | 134 | - ``init`` also accepts explicit keyword args to enable/disable various 135 | functionality – see below. 136 | 137 | To stop using Colorama before your program exits, simply call ``deinit()``. 138 | This will restore ``stdout`` and ``stderr`` to their original values, so that 139 | Colorama is disabled. To resume using Colorama again, call ``reinit()``; it is 140 | cheaper than calling ``init()`` again (but does the same thing). 141 | 142 | Most users should depend on ``colorama >= 0.4.6``, and use 143 | ``just_fix_windows_console``. The old ``init`` interface will be supported 144 | indefinitely for backwards compatibility, but we don't plan to fix any issues 145 | with it, also for backwards compatibility. 146 | 147 | Colored Output 148 | .............. 149 | 150 | Cross-platform printing of colored text can then be done using Colorama's 151 | constant shorthand for ANSI escape sequences. These are deliberately 152 | rudimentary, see below. 153 | 154 | .. code-block:: python 155 | 156 | from colorama import Fore, Back, Style 157 | print(Fore.RED + 'some red text') 158 | print(Back.GREEN + 'and with a green background') 159 | print(Style.DIM + 'and in dim text') 160 | print(Style.RESET_ALL) 161 | print('back to normal now') 162 | 163 | ...or simply by manually printing ANSI sequences from your own code: 164 | 165 | .. code-block:: python 166 | 167 | print('\033[31m' + 'some red text') 168 | print('\033[39m') # and reset to default color 169 | 170 | ...or, Colorama can be used in conjunction with existing ANSI libraries 171 | such as the venerable `Termcolor `_ 172 | the fabulous `Blessings `_, 173 | or the incredible `_Rich `_. 174 | 175 | If you wish Colorama's Fore, Back and Style constants were more capable, 176 | then consider using one of the above highly capable libraries to generate 177 | colors, etc, and use Colorama just for its primary purpose: to convert 178 | those ANSI sequences to also work on Windows: 179 | 180 | SIMILARLY, do not send PRs adding the generation of new ANSI types to Colorama. 181 | We are only interested in converting ANSI codes to win32 API calls, not 182 | shortcuts like the above to generate ANSI characters. 183 | 184 | .. code-block:: python 185 | 186 | from colorama import just_fix_windows_console 187 | from termcolor import colored 188 | 189 | # use Colorama to make Termcolor work on Windows too 190 | just_fix_windows_console() 191 | 192 | # then use Termcolor for all colored text output 193 | print(colored('Hello, World!', 'green', 'on_red')) 194 | 195 | Available formatting constants are:: 196 | 197 | Fore: BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, RESET. 198 | Back: BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE, RESET. 199 | Style: DIM, NORMAL, BRIGHT, RESET_ALL 200 | 201 | ``Style.RESET_ALL`` resets foreground, background, and brightness. Colorama will 202 | perform this reset automatically on program exit. 203 | 204 | These are fairly well supported, but not part of the standard:: 205 | 206 | Fore: LIGHTBLACK_EX, LIGHTRED_EX, LIGHTGREEN_EX, LIGHTYELLOW_EX, LIGHTBLUE_EX, LIGHTMAGENTA_EX, LIGHTCYAN_EX, LIGHTWHITE_EX 207 | Back: LIGHTBLACK_EX, LIGHTRED_EX, LIGHTGREEN_EX, LIGHTYELLOW_EX, LIGHTBLUE_EX, LIGHTMAGENTA_EX, LIGHTCYAN_EX, LIGHTWHITE_EX 208 | 209 | Cursor Positioning 210 | .................. 211 | 212 | ANSI codes to reposition the cursor are supported. See ``demos/demo06.py`` for 213 | an example of how to generate them. 214 | 215 | Init Keyword Args 216 | ................. 217 | 218 | ``init()`` accepts some ``**kwargs`` to override default behaviour. 219 | 220 | init(autoreset=False): 221 | If you find yourself repeatedly sending reset sequences to turn off color 222 | changes at the end of every print, then ``init(autoreset=True)`` will 223 | automate that: 224 | 225 | .. code-block:: python 226 | 227 | from colorama import init 228 | init(autoreset=True) 229 | print(Fore.RED + 'some red text') 230 | print('automatically back to default color again') 231 | 232 | init(strip=None): 233 | Pass ``True`` or ``False`` to override whether ANSI codes should be 234 | stripped from the output. The default behaviour is to strip if on Windows 235 | or if output is redirected (not a tty). 236 | 237 | init(convert=None): 238 | Pass ``True`` or ``False`` to override whether to convert ANSI codes in the 239 | output into win32 calls. The default behaviour is to convert if on Windows 240 | and output is to a tty (terminal). 241 | 242 | init(wrap=True): 243 | On Windows, Colorama works by replacing ``sys.stdout`` and ``sys.stderr`` 244 | with proxy objects, which override the ``.write()`` method to do their work. 245 | If this wrapping causes you problems, then this can be disabled by passing 246 | ``init(wrap=False)``. The default behaviour is to wrap if ``autoreset`` or 247 | ``strip`` or ``convert`` are True. 248 | 249 | When wrapping is disabled, colored printing on non-Windows platforms will 250 | continue to work as normal. To do cross-platform colored output, you can 251 | use Colorama's ``AnsiToWin32`` proxy directly: 252 | 253 | .. code-block:: python 254 | 255 | import sys 256 | from colorama import init, AnsiToWin32 257 | init(wrap=False) 258 | stream = AnsiToWin32(sys.stderr).stream 259 | 260 | # Python 2 261 | print >>stream, Fore.BLUE + 'blue text on stderr' 262 | 263 | # Python 3 264 | print(Fore.BLUE + 'blue text on stderr', file=stream) 265 | 266 | Recognised ANSI Sequences 267 | ......................... 268 | 269 | ANSI sequences generally take the form:: 270 | 271 | ESC [ ; ... 272 | 273 | Where ```` is an integer, and ```` is a single letter. Zero or 274 | more params are passed to a ````. If no params are passed, it is 275 | generally synonymous with passing a single zero. No spaces exist in the 276 | sequence; they have been inserted here simply to read more easily. 277 | 278 | The only ANSI sequences that Colorama converts into win32 calls are:: 279 | 280 | ESC [ 0 m # reset all (colors and brightness) 281 | ESC [ 1 m # bright 282 | ESC [ 2 m # dim (looks same as normal brightness) 283 | ESC [ 22 m # normal brightness 284 | 285 | # FOREGROUND: 286 | ESC [ 30 m # black 287 | ESC [ 31 m # red 288 | ESC [ 32 m # green 289 | ESC [ 33 m # yellow 290 | ESC [ 34 m # blue 291 | ESC [ 35 m # magenta 292 | ESC [ 36 m # cyan 293 | ESC [ 37 m # white 294 | ESC [ 39 m # reset 295 | 296 | # BACKGROUND 297 | ESC [ 40 m # black 298 | ESC [ 41 m # red 299 | ESC [ 42 m # green 300 | ESC [ 43 m # yellow 301 | ESC [ 44 m # blue 302 | ESC [ 45 m # magenta 303 | ESC [ 46 m # cyan 304 | ESC [ 47 m # white 305 | ESC [ 49 m # reset 306 | 307 | # cursor positioning 308 | ESC [ y;x H # position cursor at x across, y down 309 | ESC [ y;x f # position cursor at x across, y down 310 | ESC [ n A # move cursor n lines up 311 | ESC [ n B # move cursor n lines down 312 | ESC [ n C # move cursor n characters forward 313 | ESC [ n D # move cursor n characters backward 314 | 315 | # clear the screen 316 | ESC [ mode J # clear the screen 317 | 318 | # clear the line 319 | ESC [ mode K # clear the line 320 | 321 | Multiple numeric params to the ``'m'`` command can be combined into a single 322 | sequence:: 323 | 324 | ESC [ 36 ; 45 ; 1 m # bright cyan text on magenta background 325 | 326 | All other ANSI sequences of the form ``ESC [ ; ... `` 327 | are silently stripped from the output on Windows. 328 | 329 | Any other form of ANSI sequence, such as single-character codes or alternative 330 | initial characters, are not recognised or stripped. It would be cool to add 331 | them though. Let me know if it would be useful for you, via the Issues on 332 | GitHub. 333 | 334 | Status & Known Problems 335 | ----------------------- 336 | 337 | I've personally only tested it on Windows XP (CMD, Console2), Ubuntu 338 | (gnome-terminal, xterm), and OS X. 339 | 340 | Some valid ANSI sequences aren't recognised. 341 | 342 | If you're hacking on the code, see `README-hacking.md`_. ESPECIALLY, see the 343 | explanation there of why we do not want PRs that allow Colorama to generate new 344 | types of ANSI codes. 345 | 346 | See outstanding issues and wish-list: 347 | https://github.com/tartley/colorama/issues 348 | 349 | If anything doesn't work for you, or doesn't do what you expected or hoped for, 350 | I'd love to hear about it on that issues list, would be delighted by patches, 351 | and would be happy to grant commit access to anyone who submits a working patch 352 | or two. 353 | 354 | .. _README-hacking.md: README-hacking.md 355 | 356 | License 357 | ------- 358 | 359 | Copyright Jonathan Hartley & Arnon Yaari, 2013-2020. BSD 3-Clause license; see 360 | LICENSE file. 361 | 362 | Professional support 363 | -------------------- 364 | 365 | .. |tideliftlogo| image:: https://cdn2.hubspot.net/hubfs/4008838/website/logos/logos_for_download/Tidelift_primary-shorthand-logo.png 366 | :alt: Tidelift 367 | :target: https://tidelift.com/subscription/pkg/pypi-colorama?utm_source=pypi-colorama&utm_medium=referral&utm_campaign=readme 368 | 369 | .. list-table:: 370 | :widths: 10 100 371 | 372 | * - |tideliftlogo| 373 | - Professional support for colorama is available as part of the 374 | `Tidelift Subscription`_. 375 | Tidelift gives software development teams a single source for purchasing 376 | and maintaining their software, with professional grade assurances from 377 | the experts who know it best, while seamlessly integrating with existing 378 | tools. 379 | 380 | .. _Tidelift Subscription: https://tidelift.com/subscription/pkg/pypi-colorama?utm_source=pypi-colorama&utm_medium=referral&utm_campaign=readme 381 | 382 | Thanks 383 | ------ 384 | 385 | See the CHANGELOG for more thanks! 386 | 387 | * Marc Schlaich (schlamar) for a ``setup.py`` fix for Python2.5. 388 | * Marc Abramowitz, reported & fixed a crash on exit with closed ``stdout``, 389 | providing a solution to issue #7's setuptools/distutils debate, 390 | and other fixes. 391 | * User 'eryksun', for guidance on correctly instantiating ``ctypes.windll``. 392 | * Matthew McCormick for politely pointing out a longstanding crash on non-Win. 393 | * Ben Hoyt, for a magnificent fix under 64-bit Windows. 394 | * Jesse at Empty Square for submitting a fix for examples in the README. 395 | * User 'jamessp', an observant documentation fix for cursor positioning. 396 | * User 'vaal1239', Dave Mckee & Lackner Kristof for a tiny but much-needed Win7 397 | fix. 398 | * Julien Stuyck, for wisely suggesting Python3 compatible updates to README. 399 | * Daniel Griffith for multiple fabulous patches. 400 | * Oscar Lesta for a valuable fix to stop ANSI chars being sent to non-tty 401 | output. 402 | * Roger Binns, for many suggestions, valuable feedback, & bug reports. 403 | * Tim Golden for thought and much appreciated feedback on the initial idea. 404 | * User 'Zearin' for updates to the README file. 405 | * John Szakmeister for adding support for light colors 406 | * Charles Merriam for adding documentation to demos 407 | * Jurko for a fix on 64-bit Windows CPython2.5 w/o ctypes 408 | * Florian Bruhin for a fix when stdout or stderr are None 409 | * Thomas Weininger for fixing ValueError on Windows 410 | * Remi Rampin for better Github integration and fixes to the README file 411 | * Simeon Visser for closing a file handle using 'with' and updating classifiers 412 | to include Python 3.3 and 3.4 413 | * Andy Neff for fixing RESET of LIGHT_EX colors. 414 | * Jonathan Hartley for the initial idea and implementation. 415 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security policy 2 | 3 | To report sensitive vulnerability information, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure. 4 | -------------------------------------------------------------------------------- /bootstrap.ps1: -------------------------------------------------------------------------------- 1 | $syspython="python.exe" 2 | $ve="$HOME\.virtualenvs\colorama" 3 | $bin="$ve\Scripts" 4 | 5 | echo "Create $syspython virtualenv $ve" 6 | & $syspython -m venv --clear "$ve" 7 | & $bin\python.exe -m pip install --upgrade pip 8 | & $bin\python.exe -m pip install -r requirements.txt -r requirements-dev.txt 9 | 10 | -------------------------------------------------------------------------------- /build.ps1: -------------------------------------------------------------------------------- 1 | $ve="$HOME\.virtualenvs\colorama" 2 | $bin="$ve\Scripts" 3 | 4 | & $bin\python.exe -m pip install --upgrade build 5 | & $bin\python.exe -m build 6 | -------------------------------------------------------------------------------- /clean.ps1: -------------------------------------------------------------------------------- 1 | $syspython="python.exe" 2 | $ve="$HOME\.virtualenvs\colorama" 3 | 4 | remove-item -r -fo * -I build,dist,MANIFEST,colorama.egg-info,$ve,sandbox 5 | & $syspython -Bc "import pathlib, shutil; [shutil.rmtree(p) for p in pathlib.Path('.').rglob('__pycache__')]" 6 | 7 | -------------------------------------------------------------------------------- /colorama/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. 2 | from .initialise import init, deinit, reinit, colorama_text, just_fix_windows_console 3 | from .ansi import Fore, Back, Style, Cursor 4 | from .ansitowin32 import AnsiToWin32 5 | 6 | __version__ = '0.4.7dev1' 7 | 8 | -------------------------------------------------------------------------------- /colorama/ansi.py: -------------------------------------------------------------------------------- 1 | # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. 2 | ''' 3 | This module generates ANSI character codes to printing colors to terminals. 4 | See: http://en.wikipedia.org/wiki/ANSI_escape_code 5 | ''' 6 | 7 | CSI = '\033[' 8 | OSC = '\033]' 9 | BEL = '\a' 10 | 11 | 12 | def code_to_chars(code): 13 | return CSI + str(code) + 'm' 14 | 15 | def set_title(title): 16 | return OSC + '2;' + title + BEL 17 | 18 | def clear_screen(mode=2): 19 | return CSI + str(mode) + 'J' 20 | 21 | def clear_line(mode=2): 22 | return CSI + str(mode) + 'K' 23 | 24 | 25 | class AnsiCodes(object): 26 | def __init__(self): 27 | # the subclasses declare class attributes which are numbers. 28 | # Upon instantiation we define instance attributes, which are the same 29 | # as the class attributes but wrapped with the ANSI escape sequence 30 | for name in dir(self): 31 | if not name.startswith('_'): 32 | value = getattr(self, name) 33 | setattr(self, name, code_to_chars(value)) 34 | 35 | 36 | class AnsiCursor(object): 37 | def UP(self, n=1): 38 | return CSI + str(n) + 'A' 39 | def DOWN(self, n=1): 40 | return CSI + str(n) + 'B' 41 | def FORWARD(self, n=1): 42 | return CSI + str(n) + 'C' 43 | def BACK(self, n=1): 44 | return CSI + str(n) + 'D' 45 | def POS(self, x=1, y=1): 46 | return CSI + str(y) + ';' + str(x) + 'H' 47 | 48 | 49 | class AnsiFore(AnsiCodes): 50 | BLACK = 30 51 | RED = 31 52 | GREEN = 32 53 | YELLOW = 33 54 | BLUE = 34 55 | MAGENTA = 35 56 | CYAN = 36 57 | WHITE = 37 58 | RESET = 39 59 | 60 | # These are fairly well supported, but not part of the standard. 61 | LIGHTBLACK_EX = 90 62 | LIGHTRED_EX = 91 63 | LIGHTGREEN_EX = 92 64 | LIGHTYELLOW_EX = 93 65 | LIGHTBLUE_EX = 94 66 | LIGHTMAGENTA_EX = 95 67 | LIGHTCYAN_EX = 96 68 | LIGHTWHITE_EX = 97 69 | 70 | 71 | class AnsiBack(AnsiCodes): 72 | BLACK = 40 73 | RED = 41 74 | GREEN = 42 75 | YELLOW = 43 76 | BLUE = 44 77 | MAGENTA = 45 78 | CYAN = 46 79 | WHITE = 47 80 | RESET = 49 81 | 82 | # These are fairly well supported, but not part of the standard. 83 | LIGHTBLACK_EX = 100 84 | LIGHTRED_EX = 101 85 | LIGHTGREEN_EX = 102 86 | LIGHTYELLOW_EX = 103 87 | LIGHTBLUE_EX = 104 88 | LIGHTMAGENTA_EX = 105 89 | LIGHTCYAN_EX = 106 90 | LIGHTWHITE_EX = 107 91 | 92 | 93 | class AnsiStyle(AnsiCodes): 94 | BRIGHT = 1 95 | DIM = 2 96 | NORMAL = 22 97 | RESET_ALL = 0 98 | 99 | Fore = AnsiFore() 100 | Back = AnsiBack() 101 | Style = AnsiStyle() 102 | Cursor = AnsiCursor() 103 | -------------------------------------------------------------------------------- /colorama/ansitowin32.py: -------------------------------------------------------------------------------- 1 | # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. 2 | import re 3 | import sys 4 | import os 5 | 6 | from .ansi import AnsiFore, AnsiBack, AnsiStyle, Style, BEL 7 | from .winterm import enable_vt_processing, WinTerm, WinColor, WinStyle 8 | from .win32 import windll, winapi_test 9 | 10 | 11 | winterm = None 12 | if windll is not None: 13 | winterm = WinTerm() 14 | 15 | 16 | class StreamWrapper(object): 17 | ''' 18 | Wraps a stream (such as stdout), acting as a transparent proxy for all 19 | attribute access apart from method 'write()', which is delegated to our 20 | Converter instance. 21 | ''' 22 | def __init__(self, wrapped, converter): 23 | # double-underscore everything to prevent clashes with names of 24 | # attributes on the wrapped stream object. 25 | self.__wrapped = wrapped 26 | self.__convertor = converter 27 | 28 | def __getattr__(self, name): 29 | return getattr(self.__wrapped, name) 30 | 31 | def __enter__(self, *args, **kwargs): 32 | # special method lookup bypasses __getattr__/__getattribute__, see 33 | # https://stackoverflow.com/questions/12632894/why-doesnt-getattr-work-with-exit 34 | # thus, contextlib magic methods are not proxied via __getattr__ 35 | return self.__wrapped.__enter__(*args, **kwargs) 36 | 37 | def __exit__(self, *args, **kwargs): 38 | return self.__wrapped.__exit__(*args, **kwargs) 39 | 40 | def __setstate__(self, state): 41 | self.__dict__ = state 42 | 43 | def __getstate__(self): 44 | return self.__dict__ 45 | 46 | def write(self, text): 47 | self.__convertor.write(text) 48 | 49 | def isatty(self): 50 | stream = self.__wrapped 51 | if 'PYCHARM_HOSTED' in os.environ: 52 | if stream is not None and (stream is sys.__stdout__ or stream is sys.__stderr__): 53 | return True 54 | try: 55 | stream_isatty = stream.isatty 56 | except AttributeError: 57 | return False 58 | else: 59 | return stream_isatty() 60 | 61 | @property 62 | def closed(self): 63 | stream = self.__wrapped 64 | try: 65 | return stream.closed 66 | # AttributeError in the case that the stream doesn't support being closed 67 | # ValueError for the case that the stream has already been detached when atexit runs 68 | except (AttributeError, ValueError): 69 | return True 70 | 71 | 72 | class AnsiToWin32(object): 73 | ''' 74 | Implements a 'write()' method which, on Windows, will strip ANSI character 75 | sequences from the text, and if outputting to a tty, will convert them into 76 | win32 function calls. 77 | ''' 78 | ANSI_CSI_RE = re.compile('\001?\033\\[((?:\\d|;)*)([a-zA-Z])\002?') # Control Sequence Introducer 79 | ANSI_OSC_RE = re.compile('\001?\033\\]([^\a]*)(\a)\002?') # Operating System Command 80 | 81 | def __init__(self, wrapped, convert=None, strip=None, autoreset=False): 82 | # The wrapped stream (normally sys.stdout or sys.stderr) 83 | self.wrapped = wrapped 84 | 85 | # should we reset colors to defaults after every .write() 86 | self.autoreset = autoreset 87 | 88 | # create the proxy wrapping our output stream 89 | self.stream = StreamWrapper(wrapped, self) 90 | 91 | on_windows = os.name == 'nt' 92 | # We test if the WinAPI works, because even if we are on Windows 93 | # we may be using a terminal that doesn't support the WinAPI 94 | # (e.g. Cygwin Terminal). In this case it's up to the terminal 95 | # to support the ANSI codes. 96 | conversion_supported = on_windows and winapi_test() 97 | try: 98 | fd = wrapped.fileno() 99 | except Exception: 100 | fd = -1 101 | system_has_native_ansi = not on_windows or enable_vt_processing(fd) 102 | have_tty = not self.stream.closed and self.stream.isatty() 103 | need_conversion = conversion_supported and not system_has_native_ansi 104 | 105 | # should we strip ANSI sequences from our output? 106 | if strip is None: 107 | strip = need_conversion or not have_tty 108 | self.strip = strip 109 | 110 | # should we should convert ANSI sequences into win32 calls? 111 | if convert is None: 112 | convert = need_conversion and have_tty 113 | self.convert = convert 114 | 115 | # dict of ansi codes to win32 functions and parameters 116 | self.win32_calls = self.get_win32_calls() 117 | 118 | # are we wrapping stderr? 119 | self.on_stderr = self.wrapped is sys.stderr 120 | 121 | def should_wrap(self): 122 | ''' 123 | True if this class is actually needed. If false, then the output 124 | stream will not be affected, nor will win32 calls be issued, so 125 | wrapping stdout is not actually required. This will generally be 126 | False on non-Windows platforms, unless optional functionality like 127 | autoreset has been requested using kwargs to init() 128 | ''' 129 | return self.convert or self.strip or self.autoreset 130 | 131 | def get_win32_calls(self): 132 | if self.convert and winterm: 133 | return { 134 | AnsiStyle.RESET_ALL: (winterm.reset_all, ), 135 | AnsiStyle.BRIGHT: (winterm.style, WinStyle.BRIGHT), 136 | AnsiStyle.DIM: (winterm.style, WinStyle.NORMAL), 137 | AnsiStyle.NORMAL: (winterm.style, WinStyle.NORMAL), 138 | AnsiFore.BLACK: (winterm.fore, WinColor.BLACK), 139 | AnsiFore.RED: (winterm.fore, WinColor.RED), 140 | AnsiFore.GREEN: (winterm.fore, WinColor.GREEN), 141 | AnsiFore.YELLOW: (winterm.fore, WinColor.YELLOW), 142 | AnsiFore.BLUE: (winterm.fore, WinColor.BLUE), 143 | AnsiFore.MAGENTA: (winterm.fore, WinColor.MAGENTA), 144 | AnsiFore.CYAN: (winterm.fore, WinColor.CYAN), 145 | AnsiFore.WHITE: (winterm.fore, WinColor.GREY), 146 | AnsiFore.RESET: (winterm.fore, ), 147 | AnsiFore.LIGHTBLACK_EX: (winterm.fore, WinColor.BLACK, True), 148 | AnsiFore.LIGHTRED_EX: (winterm.fore, WinColor.RED, True), 149 | AnsiFore.LIGHTGREEN_EX: (winterm.fore, WinColor.GREEN, True), 150 | AnsiFore.LIGHTYELLOW_EX: (winterm.fore, WinColor.YELLOW, True), 151 | AnsiFore.LIGHTBLUE_EX: (winterm.fore, WinColor.BLUE, True), 152 | AnsiFore.LIGHTMAGENTA_EX: (winterm.fore, WinColor.MAGENTA, True), 153 | AnsiFore.LIGHTCYAN_EX: (winterm.fore, WinColor.CYAN, True), 154 | AnsiFore.LIGHTWHITE_EX: (winterm.fore, WinColor.GREY, True), 155 | AnsiBack.BLACK: (winterm.back, WinColor.BLACK), 156 | AnsiBack.RED: (winterm.back, WinColor.RED), 157 | AnsiBack.GREEN: (winterm.back, WinColor.GREEN), 158 | AnsiBack.YELLOW: (winterm.back, WinColor.YELLOW), 159 | AnsiBack.BLUE: (winterm.back, WinColor.BLUE), 160 | AnsiBack.MAGENTA: (winterm.back, WinColor.MAGENTA), 161 | AnsiBack.CYAN: (winterm.back, WinColor.CYAN), 162 | AnsiBack.WHITE: (winterm.back, WinColor.GREY), 163 | AnsiBack.RESET: (winterm.back, ), 164 | AnsiBack.LIGHTBLACK_EX: (winterm.back, WinColor.BLACK, True), 165 | AnsiBack.LIGHTRED_EX: (winterm.back, WinColor.RED, True), 166 | AnsiBack.LIGHTGREEN_EX: (winterm.back, WinColor.GREEN, True), 167 | AnsiBack.LIGHTYELLOW_EX: (winterm.back, WinColor.YELLOW, True), 168 | AnsiBack.LIGHTBLUE_EX: (winterm.back, WinColor.BLUE, True), 169 | AnsiBack.LIGHTMAGENTA_EX: (winterm.back, WinColor.MAGENTA, True), 170 | AnsiBack.LIGHTCYAN_EX: (winterm.back, WinColor.CYAN, True), 171 | AnsiBack.LIGHTWHITE_EX: (winterm.back, WinColor.GREY, True), 172 | } 173 | return dict() 174 | 175 | def write(self, text): 176 | if self.strip or self.convert: 177 | self.write_and_convert(text) 178 | else: 179 | self.wrapped.write(text) 180 | self.wrapped.flush() 181 | if self.autoreset: 182 | self.reset_all() 183 | 184 | 185 | def reset_all(self): 186 | if self.convert: 187 | self.call_win32('m', (0,)) 188 | elif not self.strip and not self.stream.closed: 189 | self.wrapped.write(Style.RESET_ALL) 190 | 191 | 192 | def write_and_convert(self, text): 193 | ''' 194 | Write the given text to our wrapped stream, stripping any ANSI 195 | sequences from the text, and optionally converting them into win32 196 | calls. 197 | ''' 198 | cursor = 0 199 | text = self.convert_osc(text) 200 | for match in self.ANSI_CSI_RE.finditer(text): 201 | start, end = match.span() 202 | self.write_plain_text(text, cursor, start) 203 | self.convert_ansi(*match.groups()) 204 | cursor = end 205 | self.write_plain_text(text, cursor, len(text)) 206 | 207 | 208 | def write_plain_text(self, text, start, end): 209 | if start < end: 210 | self.wrapped.write(text[start:end]) 211 | self.wrapped.flush() 212 | 213 | 214 | def convert_ansi(self, paramstring, command): 215 | if self.convert: 216 | params = self.extract_params(command, paramstring) 217 | self.call_win32(command, params) 218 | 219 | 220 | def extract_params(self, command, paramstring): 221 | if command in 'Hf': 222 | params = tuple(int(p) if len(p) != 0 else 1 for p in paramstring.split(';')) 223 | while len(params) < 2: 224 | # defaults: 225 | params = params + (1,) 226 | else: 227 | params = tuple(int(p) for p in paramstring.split(';') if len(p) != 0) 228 | if len(params) == 0: 229 | # defaults: 230 | if command in 'JKm': 231 | params = (0,) 232 | elif command in 'ABCD': 233 | params = (1,) 234 | 235 | return params 236 | 237 | 238 | def call_win32(self, command, params): 239 | if command == 'm': 240 | for param in params: 241 | if param in self.win32_calls: 242 | func_args = self.win32_calls[param] 243 | func = func_args[0] 244 | args = func_args[1:] 245 | kwargs = dict(on_stderr=self.on_stderr) 246 | func(*args, **kwargs) 247 | elif command in 'J': 248 | winterm.erase_screen(params[0], on_stderr=self.on_stderr) 249 | elif command in 'K': 250 | winterm.erase_line(params[0], on_stderr=self.on_stderr) 251 | elif command in 'Hf': # cursor position - absolute 252 | winterm.set_cursor_position(params, on_stderr=self.on_stderr) 253 | elif command in 'ABCD': # cursor position - relative 254 | n = params[0] 255 | # A - up, B - down, C - forward, D - back 256 | x, y = {'A': (0, -n), 'B': (0, n), 'C': (n, 0), 'D': (-n, 0)}[command] 257 | winterm.cursor_adjust(x, y, on_stderr=self.on_stderr) 258 | 259 | 260 | def convert_osc(self, text): 261 | for match in self.ANSI_OSC_RE.finditer(text): 262 | start, end = match.span() 263 | text = text[:start] + text[end:] 264 | paramstring, command = match.groups() 265 | if command == BEL: 266 | if paramstring.count(";") == 1: 267 | params = paramstring.split(";") 268 | # 0 - change title and icon (we will only change title) 269 | # 1 - change icon (we don't support this) 270 | # 2 - change title 271 | if params[0] in '02': 272 | winterm.set_title(params[1]) 273 | return text 274 | 275 | 276 | def flush(self): 277 | self.wrapped.flush() 278 | -------------------------------------------------------------------------------- /colorama/initialise.py: -------------------------------------------------------------------------------- 1 | # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. 2 | import atexit 3 | import contextlib 4 | import sys 5 | 6 | from .ansitowin32 import AnsiToWin32 7 | 8 | 9 | def _wipe_internal_state_for_tests(): 10 | global orig_stdout, orig_stderr 11 | orig_stdout = None 12 | orig_stderr = None 13 | 14 | global wrapped_stdout, wrapped_stderr 15 | wrapped_stdout = None 16 | wrapped_stderr = None 17 | 18 | global atexit_done 19 | atexit_done = False 20 | 21 | global fixed_windows_console 22 | fixed_windows_console = False 23 | 24 | try: 25 | # no-op if it wasn't registered 26 | atexit.unregister(reset_all) 27 | except AttributeError: 28 | # python 2: no atexit.unregister. Oh well, we did our best. 29 | pass 30 | 31 | 32 | def reset_all(): 33 | if AnsiToWin32 is not None: # Issue #74: objects might become None at exit 34 | AnsiToWin32(orig_stdout).reset_all() 35 | 36 | 37 | def init(autoreset=False, convert=None, strip=None, wrap=True): 38 | 39 | if not wrap and any([autoreset, convert, strip]): 40 | raise ValueError('wrap=False conflicts with any other arg=True') 41 | 42 | global wrapped_stdout, wrapped_stderr 43 | global orig_stdout, orig_stderr 44 | 45 | orig_stdout = sys.stdout 46 | orig_stderr = sys.stderr 47 | 48 | if sys.stdout is None: 49 | wrapped_stdout = None 50 | else: 51 | sys.stdout = wrapped_stdout = \ 52 | wrap_stream(orig_stdout, convert, strip, autoreset, wrap) 53 | if sys.stderr is None: 54 | wrapped_stderr = None 55 | else: 56 | sys.stderr = wrapped_stderr = \ 57 | wrap_stream(orig_stderr, convert, strip, autoreset, wrap) 58 | 59 | global atexit_done 60 | if not atexit_done: 61 | atexit.register(reset_all) 62 | atexit_done = True 63 | 64 | 65 | def deinit(): 66 | if orig_stdout is not None: 67 | sys.stdout = orig_stdout 68 | if orig_stderr is not None: 69 | sys.stderr = orig_stderr 70 | 71 | 72 | def just_fix_windows_console(): 73 | global fixed_windows_console 74 | 75 | if sys.platform != "win32": 76 | return 77 | if fixed_windows_console: 78 | return 79 | if wrapped_stdout is not None or wrapped_stderr is not None: 80 | # Someone already ran init() and it did stuff, so we won't second-guess them 81 | return 82 | 83 | # On newer versions of Windows, AnsiToWin32.__init__ will implicitly enable the 84 | # native ANSI support in the console as a side-effect. We only need to actually 85 | # replace sys.stdout/stderr if we're in the old-style conversion mode. 86 | new_stdout = AnsiToWin32(sys.stdout, convert=None, strip=None, autoreset=False) 87 | if new_stdout.convert: 88 | sys.stdout = new_stdout 89 | new_stderr = AnsiToWin32(sys.stderr, convert=None, strip=None, autoreset=False) 90 | if new_stderr.convert: 91 | sys.stderr = new_stderr 92 | 93 | fixed_windows_console = True 94 | 95 | @contextlib.contextmanager 96 | def colorama_text(*args, **kwargs): 97 | init(*args, **kwargs) 98 | try: 99 | yield 100 | finally: 101 | deinit() 102 | 103 | 104 | def reinit(): 105 | if wrapped_stdout is not None: 106 | sys.stdout = wrapped_stdout 107 | if wrapped_stderr is not None: 108 | sys.stderr = wrapped_stderr 109 | 110 | 111 | def wrap_stream(stream, convert, strip, autoreset, wrap): 112 | if wrap: 113 | wrapper = AnsiToWin32(stream, 114 | convert=convert, strip=strip, autoreset=autoreset) 115 | if wrapper.should_wrap(): 116 | stream = wrapper.stream 117 | return stream 118 | 119 | 120 | # Use this for initial setup as well, to reduce code duplication 121 | _wipe_internal_state_for_tests() 122 | -------------------------------------------------------------------------------- /colorama/tests/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. 2 | -------------------------------------------------------------------------------- /colorama/tests/ansi_test.py: -------------------------------------------------------------------------------- 1 | # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. 2 | import sys 3 | from unittest import TestCase, main 4 | 5 | from ..ansi import Back, Fore, Style 6 | from ..ansitowin32 import AnsiToWin32 7 | 8 | stdout_orig = sys.stdout 9 | stderr_orig = sys.stderr 10 | 11 | 12 | class AnsiTest(TestCase): 13 | 14 | def setUp(self): 15 | # sanity check: stdout should be a file or StringIO object. 16 | # It will only be AnsiToWin32 if init() has previously wrapped it 17 | self.assertNotEqual(type(sys.stdout), AnsiToWin32) 18 | self.assertNotEqual(type(sys.stderr), AnsiToWin32) 19 | 20 | def tearDown(self): 21 | sys.stdout = stdout_orig 22 | sys.stderr = stderr_orig 23 | 24 | 25 | def testForeAttributes(self): 26 | self.assertEqual(Fore.BLACK, '\033[30m') 27 | self.assertEqual(Fore.RED, '\033[31m') 28 | self.assertEqual(Fore.GREEN, '\033[32m') 29 | self.assertEqual(Fore.YELLOW, '\033[33m') 30 | self.assertEqual(Fore.BLUE, '\033[34m') 31 | self.assertEqual(Fore.MAGENTA, '\033[35m') 32 | self.assertEqual(Fore.CYAN, '\033[36m') 33 | self.assertEqual(Fore.WHITE, '\033[37m') 34 | self.assertEqual(Fore.RESET, '\033[39m') 35 | 36 | # Check the light, extended versions. 37 | self.assertEqual(Fore.LIGHTBLACK_EX, '\033[90m') 38 | self.assertEqual(Fore.LIGHTRED_EX, '\033[91m') 39 | self.assertEqual(Fore.LIGHTGREEN_EX, '\033[92m') 40 | self.assertEqual(Fore.LIGHTYELLOW_EX, '\033[93m') 41 | self.assertEqual(Fore.LIGHTBLUE_EX, '\033[94m') 42 | self.assertEqual(Fore.LIGHTMAGENTA_EX, '\033[95m') 43 | self.assertEqual(Fore.LIGHTCYAN_EX, '\033[96m') 44 | self.assertEqual(Fore.LIGHTWHITE_EX, '\033[97m') 45 | 46 | 47 | def testBackAttributes(self): 48 | self.assertEqual(Back.BLACK, '\033[40m') 49 | self.assertEqual(Back.RED, '\033[41m') 50 | self.assertEqual(Back.GREEN, '\033[42m') 51 | self.assertEqual(Back.YELLOW, '\033[43m') 52 | self.assertEqual(Back.BLUE, '\033[44m') 53 | self.assertEqual(Back.MAGENTA, '\033[45m') 54 | self.assertEqual(Back.CYAN, '\033[46m') 55 | self.assertEqual(Back.WHITE, '\033[47m') 56 | self.assertEqual(Back.RESET, '\033[49m') 57 | 58 | # Check the light, extended versions. 59 | self.assertEqual(Back.LIGHTBLACK_EX, '\033[100m') 60 | self.assertEqual(Back.LIGHTRED_EX, '\033[101m') 61 | self.assertEqual(Back.LIGHTGREEN_EX, '\033[102m') 62 | self.assertEqual(Back.LIGHTYELLOW_EX, '\033[103m') 63 | self.assertEqual(Back.LIGHTBLUE_EX, '\033[104m') 64 | self.assertEqual(Back.LIGHTMAGENTA_EX, '\033[105m') 65 | self.assertEqual(Back.LIGHTCYAN_EX, '\033[106m') 66 | self.assertEqual(Back.LIGHTWHITE_EX, '\033[107m') 67 | 68 | 69 | def testStyleAttributes(self): 70 | self.assertEqual(Style.DIM, '\033[2m') 71 | self.assertEqual(Style.NORMAL, '\033[22m') 72 | self.assertEqual(Style.BRIGHT, '\033[1m') 73 | 74 | 75 | if __name__ == '__main__': 76 | main() 77 | -------------------------------------------------------------------------------- /colorama/tests/ansitowin32_test.py: -------------------------------------------------------------------------------- 1 | # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. 2 | from io import StringIO, TextIOWrapper 3 | from unittest import TestCase, main 4 | try: 5 | from contextlib import ExitStack 6 | except ImportError: 7 | # python 2 8 | from contextlib2 import ExitStack 9 | 10 | try: 11 | from unittest.mock import MagicMock, Mock, patch 12 | except ImportError: 13 | from mock import MagicMock, Mock, patch 14 | 15 | from ..ansitowin32 import AnsiToWin32, StreamWrapper 16 | from ..win32 import ENABLE_VIRTUAL_TERMINAL_PROCESSING 17 | from .utils import osname 18 | 19 | 20 | class StreamWrapperTest(TestCase): 21 | 22 | def testIsAProxy(self): 23 | mockStream = Mock() 24 | wrapper = StreamWrapper(mockStream, None) 25 | self.assertTrue( wrapper.random_attr is mockStream.random_attr ) 26 | 27 | def testDelegatesWrite(self): 28 | mockStream = Mock() 29 | mockConverter = Mock() 30 | wrapper = StreamWrapper(mockStream, mockConverter) 31 | wrapper.write('hello') 32 | self.assertTrue(mockConverter.write.call_args, (('hello',), {})) 33 | 34 | def testDelegatesContext(self): 35 | mockConverter = Mock() 36 | s = StringIO() 37 | with StreamWrapper(s, mockConverter) as fp: 38 | fp.write(u'hello') 39 | self.assertTrue(s.closed) 40 | 41 | def testProxyNoContextManager(self): 42 | mockStream = MagicMock() 43 | mockStream.__enter__.side_effect = AttributeError() 44 | mockConverter = Mock() 45 | with self.assertRaises(AttributeError) as excinfo: 46 | with StreamWrapper(mockStream, mockConverter) as wrapper: 47 | wrapper.write('hello') 48 | 49 | def test_closed_shouldnt_raise_on_closed_stream(self): 50 | stream = StringIO() 51 | stream.close() 52 | wrapper = StreamWrapper(stream, None) 53 | self.assertEqual(wrapper.closed, True) 54 | 55 | def test_closed_shouldnt_raise_on_detached_stream(self): 56 | stream = TextIOWrapper(StringIO()) 57 | stream.detach() 58 | wrapper = StreamWrapper(stream, None) 59 | self.assertEqual(wrapper.closed, True) 60 | 61 | class AnsiToWin32Test(TestCase): 62 | 63 | def testInit(self): 64 | mockStdout = Mock() 65 | auto = Mock() 66 | stream = AnsiToWin32(mockStdout, autoreset=auto) 67 | self.assertEqual(stream.wrapped, mockStdout) 68 | self.assertEqual(stream.autoreset, auto) 69 | 70 | @patch('colorama.ansitowin32.winterm', None) 71 | @patch('colorama.ansitowin32.winapi_test', lambda *_: True) 72 | def testStripIsTrueOnWindows(self): 73 | with osname('nt'): 74 | mockStdout = Mock() 75 | stream = AnsiToWin32(mockStdout) 76 | self.assertTrue(stream.strip) 77 | 78 | def testStripIsFalseOffWindows(self): 79 | with osname('posix'): 80 | mockStdout = Mock(closed=False) 81 | stream = AnsiToWin32(mockStdout) 82 | self.assertFalse(stream.strip) 83 | 84 | def testWriteStripsAnsi(self): 85 | mockStdout = Mock() 86 | stream = AnsiToWin32(mockStdout) 87 | stream.wrapped = Mock() 88 | stream.write_and_convert = Mock() 89 | stream.strip = True 90 | 91 | stream.write('abc') 92 | 93 | self.assertFalse(stream.wrapped.write.called) 94 | self.assertEqual(stream.write_and_convert.call_args, (('abc',), {})) 95 | 96 | def testWriteDoesNotStripAnsi(self): 97 | mockStdout = Mock() 98 | stream = AnsiToWin32(mockStdout) 99 | stream.wrapped = Mock() 100 | stream.write_and_convert = Mock() 101 | stream.strip = False 102 | stream.convert = False 103 | 104 | stream.write('abc') 105 | 106 | self.assertFalse(stream.write_and_convert.called) 107 | self.assertEqual(stream.wrapped.write.call_args, (('abc',), {})) 108 | 109 | def assert_autoresets(self, convert, autoreset=True): 110 | stream = AnsiToWin32(Mock()) 111 | stream.convert = convert 112 | stream.reset_all = Mock() 113 | stream.autoreset = autoreset 114 | stream.winterm = Mock() 115 | 116 | stream.write('abc') 117 | 118 | self.assertEqual(stream.reset_all.called, autoreset) 119 | 120 | def testWriteAutoresets(self): 121 | self.assert_autoresets(convert=True) 122 | self.assert_autoresets(convert=False) 123 | self.assert_autoresets(convert=True, autoreset=False) 124 | self.assert_autoresets(convert=False, autoreset=False) 125 | 126 | def testWriteAndConvertWritesPlainText(self): 127 | stream = AnsiToWin32(Mock()) 128 | stream.write_and_convert( 'abc' ) 129 | self.assertEqual( stream.wrapped.write.call_args, (('abc',), {}) ) 130 | 131 | def testWriteAndConvertStripsAllValidAnsi(self): 132 | stream = AnsiToWin32(Mock()) 133 | stream.call_win32 = Mock() 134 | data = [ 135 | 'abc\033[mdef', 136 | 'abc\033[0mdef', 137 | 'abc\033[2mdef', 138 | 'abc\033[02mdef', 139 | 'abc\033[002mdef', 140 | 'abc\033[40mdef', 141 | 'abc\033[040mdef', 142 | 'abc\033[0;1mdef', 143 | 'abc\033[40;50mdef', 144 | 'abc\033[50;30;40mdef', 145 | 'abc\033[Adef', 146 | 'abc\033[0Gdef', 147 | 'abc\033[1;20;128Hdef', 148 | ] 149 | for datum in data: 150 | stream.wrapped.write.reset_mock() 151 | stream.write_and_convert( datum ) 152 | self.assertEqual( 153 | [args[0] for args in stream.wrapped.write.call_args_list], 154 | [ ('abc',), ('def',) ] 155 | ) 156 | 157 | def testWriteAndConvertSkipsEmptySnippets(self): 158 | stream = AnsiToWin32(Mock()) 159 | stream.call_win32 = Mock() 160 | stream.write_and_convert( '\033[40m\033[41m' ) 161 | self.assertFalse( stream.wrapped.write.called ) 162 | 163 | def testWriteAndConvertCallsWin32WithParamsAndCommand(self): 164 | stream = AnsiToWin32(Mock()) 165 | stream.convert = True 166 | stream.call_win32 = Mock() 167 | stream.extract_params = Mock(return_value='params') 168 | data = { 169 | 'abc\033[adef': ('a', 'params'), 170 | 'abc\033[;;bdef': ('b', 'params'), 171 | 'abc\033[0cdef': ('c', 'params'), 172 | 'abc\033[;;0;;Gdef': ('G', 'params'), 173 | 'abc\033[1;20;128Hdef': ('H', 'params'), 174 | } 175 | for datum, expected in data.items(): 176 | stream.call_win32.reset_mock() 177 | stream.write_and_convert( datum ) 178 | self.assertEqual( stream.call_win32.call_args[0], expected ) 179 | 180 | def test_reset_all_shouldnt_raise_on_closed_orig_stdout(self): 181 | stream = StringIO() 182 | converter = AnsiToWin32(stream) 183 | stream.close() 184 | 185 | converter.reset_all() 186 | 187 | def test_wrap_shouldnt_raise_on_closed_orig_stdout(self): 188 | stream = StringIO() 189 | stream.close() 190 | with \ 191 | patch("colorama.ansitowin32.os.name", "nt"), \ 192 | patch("colorama.ansitowin32.winapi_test", lambda: True): 193 | converter = AnsiToWin32(stream) 194 | self.assertTrue(converter.strip) 195 | self.assertFalse(converter.convert) 196 | 197 | def test_wrap_shouldnt_raise_on_missing_closed_attr(self): 198 | with \ 199 | patch("colorama.ansitowin32.os.name", "nt"), \ 200 | patch("colorama.ansitowin32.winapi_test", lambda: True): 201 | converter = AnsiToWin32(object()) 202 | self.assertTrue(converter.strip) 203 | self.assertFalse(converter.convert) 204 | 205 | def testExtractParams(self): 206 | stream = AnsiToWin32(Mock()) 207 | data = { 208 | '': (0,), 209 | ';;': (0,), 210 | '2': (2,), 211 | ';;002;;': (2,), 212 | '0;1': (0, 1), 213 | ';;003;;456;;': (3, 456), 214 | '11;22;33;44;55': (11, 22, 33, 44, 55), 215 | } 216 | for datum, expected in data.items(): 217 | self.assertEqual(stream.extract_params('m', datum), expected) 218 | 219 | def testCallWin32UsesLookup(self): 220 | listener = Mock() 221 | stream = AnsiToWin32(listener) 222 | stream.win32_calls = { 223 | 1: (lambda *_, **__: listener(11),), 224 | 2: (lambda *_, **__: listener(22),), 225 | 3: (lambda *_, **__: listener(33),), 226 | } 227 | stream.call_win32('m', (3, 1, 99, 2)) 228 | self.assertEqual( 229 | [a[0][0] for a in listener.call_args_list], 230 | [33, 11, 22] ) 231 | 232 | def test_osc_codes(self): 233 | mockStdout = Mock() 234 | stream = AnsiToWin32(mockStdout, convert=True) 235 | with patch('colorama.ansitowin32.winterm') as winterm: 236 | data = [ 237 | '\033]0\x07', # missing arguments 238 | '\033]0;foo\x08', # wrong OSC command 239 | '\033]0;colorama_test_title\x07', # should work 240 | '\033]1;colorama_test_title\x07', # wrong set command 241 | '\033]2;colorama_test_title\x07', # should work 242 | '\033]' + ';' * 64 + '\x08', # see issue #247 243 | ] 244 | for code in data: 245 | stream.write(code) 246 | self.assertEqual(winterm.set_title.call_count, 2) 247 | 248 | def test_native_windows_ansi(self): 249 | with ExitStack() as stack: 250 | def p(a, b): 251 | stack.enter_context(patch(a, b, create=True)) 252 | # Pretend to be on Windows 253 | p("colorama.ansitowin32.os.name", "nt") 254 | p("colorama.ansitowin32.winapi_test", lambda: True) 255 | p("colorama.win32.winapi_test", lambda: True) 256 | p("colorama.winterm.win32.windll", "non-None") 257 | p("colorama.winterm.get_osfhandle", lambda _: 1234) 258 | 259 | # Pretend that our mock stream has native ANSI support 260 | p( 261 | "colorama.winterm.win32.GetConsoleMode", 262 | lambda _: ENABLE_VIRTUAL_TERMINAL_PROCESSING, 263 | ) 264 | SetConsoleMode = Mock() 265 | p("colorama.winterm.win32.SetConsoleMode", SetConsoleMode) 266 | 267 | stdout = Mock() 268 | stdout.closed = False 269 | stdout.isatty.return_value = True 270 | stdout.fileno.return_value = 1 271 | 272 | # Our fake console says it has native vt support, so AnsiToWin32 should 273 | # enable that support and do nothing else. 274 | stream = AnsiToWin32(stdout) 275 | SetConsoleMode.assert_called_with(1234, ENABLE_VIRTUAL_TERMINAL_PROCESSING) 276 | self.assertFalse(stream.strip) 277 | self.assertFalse(stream.convert) 278 | self.assertFalse(stream.should_wrap()) 279 | 280 | # Now let's pretend we're on an old Windows console, that doesn't have 281 | # native ANSI support. 282 | p("colorama.winterm.win32.GetConsoleMode", lambda _: 0) 283 | SetConsoleMode = Mock() 284 | p("colorama.winterm.win32.SetConsoleMode", SetConsoleMode) 285 | 286 | stream = AnsiToWin32(stdout) 287 | SetConsoleMode.assert_called_with(1234, ENABLE_VIRTUAL_TERMINAL_PROCESSING) 288 | self.assertTrue(stream.strip) 289 | self.assertTrue(stream.convert) 290 | self.assertTrue(stream.should_wrap()) 291 | 292 | 293 | if __name__ == '__main__': 294 | main() 295 | -------------------------------------------------------------------------------- /colorama/tests/initialise_test.py: -------------------------------------------------------------------------------- 1 | # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. 2 | import sys 3 | from unittest import TestCase, main, skipUnless 4 | 5 | try: 6 | from unittest.mock import patch, Mock 7 | except ImportError: 8 | from mock import patch, Mock 9 | 10 | from ..ansitowin32 import StreamWrapper 11 | from ..initialise import init, just_fix_windows_console, _wipe_internal_state_for_tests 12 | from .utils import osname, replace_by 13 | 14 | orig_stdout = sys.stdout 15 | orig_stderr = sys.stderr 16 | 17 | 18 | class InitTest(TestCase): 19 | 20 | @skipUnless(sys.stdout.isatty(), "sys.stdout is not a tty") 21 | def setUp(self): 22 | # sanity check 23 | self.assertNotWrapped() 24 | 25 | def tearDown(self): 26 | _wipe_internal_state_for_tests() 27 | sys.stdout = orig_stdout 28 | sys.stderr = orig_stderr 29 | 30 | def assertWrapped(self): 31 | self.assertIsNot(sys.stdout, orig_stdout, 'stdout should be wrapped') 32 | self.assertIsNot(sys.stderr, orig_stderr, 'stderr should be wrapped') 33 | self.assertTrue(isinstance(sys.stdout, StreamWrapper), 34 | 'bad stdout wrapper') 35 | self.assertTrue(isinstance(sys.stderr, StreamWrapper), 36 | 'bad stderr wrapper') 37 | 38 | def assertNotWrapped(self): 39 | self.assertIs(sys.stdout, orig_stdout, 'stdout should not be wrapped') 40 | self.assertIs(sys.stderr, orig_stderr, 'stderr should not be wrapped') 41 | 42 | @patch('colorama.initialise.reset_all') 43 | @patch('colorama.ansitowin32.winapi_test', lambda *_: True) 44 | @patch('colorama.ansitowin32.enable_vt_processing', lambda *_: False) 45 | def testInitWrapsOnWindows(self, _): 46 | with osname("nt"): 47 | init() 48 | self.assertWrapped() 49 | 50 | @patch('colorama.initialise.reset_all') 51 | @patch('colorama.ansitowin32.winapi_test', lambda *_: False) 52 | def testInitDoesntWrapOnEmulatedWindows(self, _): 53 | with osname("nt"): 54 | init() 55 | self.assertNotWrapped() 56 | 57 | def testInitDoesntWrapOnNonWindows(self): 58 | with osname("posix"): 59 | init() 60 | self.assertNotWrapped() 61 | 62 | def testInitDoesntWrapIfNone(self): 63 | with replace_by(None): 64 | init() 65 | # We can't use assertNotWrapped here because replace_by(None) 66 | # changes stdout/stderr already. 67 | self.assertIsNone(sys.stdout) 68 | self.assertIsNone(sys.stderr) 69 | 70 | def testInitAutoresetOnWrapsOnAllPlatforms(self): 71 | with osname("posix"): 72 | init(autoreset=True) 73 | self.assertWrapped() 74 | 75 | def testInitWrapOffDoesntWrapOnWindows(self): 76 | with osname("nt"): 77 | init(wrap=False) 78 | self.assertNotWrapped() 79 | 80 | def testInitWrapOffIncompatibleWithAutoresetOn(self): 81 | self.assertRaises(ValueError, lambda: init(autoreset=True, wrap=False)) 82 | 83 | @patch('colorama.win32.SetConsoleTextAttribute') 84 | @patch('colorama.initialise.AnsiToWin32') 85 | def testAutoResetPassedOn(self, mockATW32, _): 86 | with osname("nt"): 87 | init(autoreset=True) 88 | self.assertEqual(len(mockATW32.call_args_list), 2) 89 | self.assertEqual(mockATW32.call_args_list[1][1]['autoreset'], True) 90 | self.assertEqual(mockATW32.call_args_list[0][1]['autoreset'], True) 91 | 92 | @patch('colorama.initialise.AnsiToWin32') 93 | def testAutoResetChangeable(self, mockATW32): 94 | with osname("nt"): 95 | init() 96 | 97 | init(autoreset=True) 98 | self.assertEqual(len(mockATW32.call_args_list), 4) 99 | self.assertEqual(mockATW32.call_args_list[2][1]['autoreset'], True) 100 | self.assertEqual(mockATW32.call_args_list[3][1]['autoreset'], True) 101 | 102 | init() 103 | self.assertEqual(len(mockATW32.call_args_list), 6) 104 | self.assertEqual( 105 | mockATW32.call_args_list[4][1]['autoreset'], False) 106 | self.assertEqual( 107 | mockATW32.call_args_list[5][1]['autoreset'], False) 108 | 109 | 110 | @patch('colorama.initialise.atexit.register') 111 | def testAtexitRegisteredOnlyOnce(self, mockRegister): 112 | init() 113 | self.assertTrue(mockRegister.called) 114 | mockRegister.reset_mock() 115 | init() 116 | self.assertFalse(mockRegister.called) 117 | 118 | 119 | class JustFixWindowsConsoleTest(TestCase): 120 | def _reset(self): 121 | _wipe_internal_state_for_tests() 122 | sys.stdout = orig_stdout 123 | sys.stderr = orig_stderr 124 | 125 | def tearDown(self): 126 | self._reset() 127 | 128 | @patch("colorama.ansitowin32.winapi_test", lambda: True) 129 | def testJustFixWindowsConsole(self): 130 | if sys.platform != "win32": 131 | # just_fix_windows_console should be a no-op 132 | just_fix_windows_console() 133 | self.assertIs(sys.stdout, orig_stdout) 134 | self.assertIs(sys.stderr, orig_stderr) 135 | else: 136 | def fake_std(): 137 | # Emulate stdout=not a tty, stderr=tty 138 | # to check that we handle both cases correctly 139 | stdout = Mock() 140 | stdout.closed = False 141 | stdout.isatty.return_value = False 142 | stdout.fileno.return_value = 1 143 | sys.stdout = stdout 144 | 145 | stderr = Mock() 146 | stderr.closed = False 147 | stderr.isatty.return_value = True 148 | stderr.fileno.return_value = 2 149 | sys.stderr = stderr 150 | 151 | for native_ansi in [False, True]: 152 | with patch( 153 | 'colorama.ansitowin32.enable_vt_processing', 154 | lambda *_: native_ansi 155 | ): 156 | self._reset() 157 | fake_std() 158 | 159 | # Regular single-call test 160 | prev_stdout = sys.stdout 161 | prev_stderr = sys.stderr 162 | just_fix_windows_console() 163 | self.assertIs(sys.stdout, prev_stdout) 164 | if native_ansi: 165 | self.assertIs(sys.stderr, prev_stderr) 166 | else: 167 | self.assertIsNot(sys.stderr, prev_stderr) 168 | 169 | # second call without resetting is always a no-op 170 | prev_stdout = sys.stdout 171 | prev_stderr = sys.stderr 172 | just_fix_windows_console() 173 | self.assertIs(sys.stdout, prev_stdout) 174 | self.assertIs(sys.stderr, prev_stderr) 175 | 176 | self._reset() 177 | fake_std() 178 | 179 | # If init() runs first, just_fix_windows_console should be a no-op 180 | init() 181 | prev_stdout = sys.stdout 182 | prev_stderr = sys.stderr 183 | just_fix_windows_console() 184 | self.assertIs(prev_stdout, sys.stdout) 185 | self.assertIs(prev_stderr, sys.stderr) 186 | 187 | 188 | if __name__ == '__main__': 189 | main() 190 | -------------------------------------------------------------------------------- /colorama/tests/isatty_test.py: -------------------------------------------------------------------------------- 1 | # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. 2 | import sys 3 | from unittest import TestCase, main 4 | 5 | from ..ansitowin32 import StreamWrapper, AnsiToWin32 6 | from .utils import pycharm, replace_by, replace_original_by, StreamTTY, StreamNonTTY 7 | 8 | 9 | def is_a_tty(stream): 10 | return StreamWrapper(stream, None).isatty() 11 | 12 | class IsattyTest(TestCase): 13 | 14 | def test_TTY(self): 15 | tty = StreamTTY() 16 | self.assertTrue(is_a_tty(tty)) 17 | with pycharm(): 18 | self.assertTrue(is_a_tty(tty)) 19 | 20 | def test_nonTTY(self): 21 | non_tty = StreamNonTTY() 22 | self.assertFalse(is_a_tty(non_tty)) 23 | with pycharm(): 24 | self.assertFalse(is_a_tty(non_tty)) 25 | 26 | def test_withPycharm(self): 27 | with pycharm(): 28 | self.assertTrue(is_a_tty(sys.stderr)) 29 | self.assertTrue(is_a_tty(sys.stdout)) 30 | 31 | def test_withPycharmTTYOverride(self): 32 | tty = StreamTTY() 33 | with pycharm(), replace_by(tty): 34 | self.assertTrue(is_a_tty(tty)) 35 | 36 | def test_withPycharmNonTTYOverride(self): 37 | non_tty = StreamNonTTY() 38 | with pycharm(), replace_by(non_tty): 39 | self.assertFalse(is_a_tty(non_tty)) 40 | 41 | def test_withPycharmNoneOverride(self): 42 | with pycharm(): 43 | with replace_by(None), replace_original_by(None): 44 | self.assertFalse(is_a_tty(None)) 45 | self.assertFalse(is_a_tty(StreamNonTTY())) 46 | self.assertTrue(is_a_tty(StreamTTY())) 47 | 48 | def test_withPycharmStreamWrapped(self): 49 | with pycharm(): 50 | self.assertTrue(AnsiToWin32(StreamTTY()).stream.isatty()) 51 | self.assertFalse(AnsiToWin32(StreamNonTTY()).stream.isatty()) 52 | self.assertTrue(AnsiToWin32(sys.stdout).stream.isatty()) 53 | self.assertTrue(AnsiToWin32(sys.stderr).stream.isatty()) 54 | 55 | 56 | if __name__ == '__main__': 57 | main() 58 | -------------------------------------------------------------------------------- /colorama/tests/utils.py: -------------------------------------------------------------------------------- 1 | # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. 2 | from contextlib import contextmanager 3 | from io import StringIO 4 | import sys 5 | import os 6 | 7 | 8 | class StreamTTY(StringIO): 9 | def isatty(self): 10 | return True 11 | 12 | class StreamNonTTY(StringIO): 13 | def isatty(self): 14 | return False 15 | 16 | @contextmanager 17 | def osname(name): 18 | orig = os.name 19 | os.name = name 20 | yield 21 | os.name = orig 22 | 23 | @contextmanager 24 | def replace_by(stream): 25 | orig_stdout = sys.stdout 26 | orig_stderr = sys.stderr 27 | sys.stdout = stream 28 | sys.stderr = stream 29 | yield 30 | sys.stdout = orig_stdout 31 | sys.stderr = orig_stderr 32 | 33 | @contextmanager 34 | def replace_original_by(stream): 35 | orig_stdout = sys.__stdout__ 36 | orig_stderr = sys.__stderr__ 37 | sys.__stdout__ = stream 38 | sys.__stderr__ = stream 39 | yield 40 | sys.__stdout__ = orig_stdout 41 | sys.__stderr__ = orig_stderr 42 | 43 | @contextmanager 44 | def pycharm(): 45 | os.environ["PYCHARM_HOSTED"] = "1" 46 | non_tty = StreamNonTTY() 47 | with replace_by(non_tty), replace_original_by(non_tty): 48 | yield 49 | del os.environ["PYCHARM_HOSTED"] 50 | -------------------------------------------------------------------------------- /colorama/tests/winterm_test.py: -------------------------------------------------------------------------------- 1 | # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. 2 | import sys 3 | from unittest import TestCase, main, skipUnless 4 | 5 | try: 6 | from unittest.mock import Mock, patch 7 | except ImportError: 8 | from mock import Mock, patch 9 | 10 | from ..winterm import WinColor, WinStyle, WinTerm 11 | 12 | 13 | class WinTermTest(TestCase): 14 | 15 | @patch('colorama.winterm.win32') 16 | def testInit(self, mockWin32): 17 | mockAttr = Mock() 18 | mockAttr.wAttributes = 7 + 6 * 16 + 8 19 | mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr 20 | term = WinTerm() 21 | self.assertEqual(term._fore, 7) 22 | self.assertEqual(term._back, 6) 23 | self.assertEqual(term._style, 8) 24 | 25 | @skipUnless(sys.platform.startswith("win"), "requires Windows") 26 | def testGetAttrs(self): 27 | term = WinTerm() 28 | 29 | term._fore = 0 30 | term._back = 0 31 | term._style = 0 32 | self.assertEqual(term.get_attrs(), 0) 33 | 34 | term._fore = WinColor.YELLOW 35 | self.assertEqual(term.get_attrs(), WinColor.YELLOW) 36 | 37 | term._back = WinColor.MAGENTA 38 | self.assertEqual( 39 | term.get_attrs(), 40 | WinColor.YELLOW + WinColor.MAGENTA * 16) 41 | 42 | term._style = WinStyle.BRIGHT 43 | self.assertEqual( 44 | term.get_attrs(), 45 | WinColor.YELLOW + WinColor.MAGENTA * 16 + WinStyle.BRIGHT) 46 | 47 | @patch('colorama.winterm.win32') 48 | def testResetAll(self, mockWin32): 49 | mockAttr = Mock() 50 | mockAttr.wAttributes = 1 + 2 * 16 + 8 51 | mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr 52 | term = WinTerm() 53 | 54 | term.set_console = Mock() 55 | term._fore = -1 56 | term._back = -1 57 | term._style = -1 58 | 59 | term.reset_all() 60 | 61 | self.assertEqual(term._fore, 1) 62 | self.assertEqual(term._back, 2) 63 | self.assertEqual(term._style, 8) 64 | self.assertEqual(term.set_console.called, True) 65 | 66 | @skipUnless(sys.platform.startswith("win"), "requires Windows") 67 | def testFore(self): 68 | term = WinTerm() 69 | term.set_console = Mock() 70 | term._fore = 0 71 | 72 | term.fore(5) 73 | 74 | self.assertEqual(term._fore, 5) 75 | self.assertEqual(term.set_console.called, True) 76 | 77 | @skipUnless(sys.platform.startswith("win"), "requires Windows") 78 | def testBack(self): 79 | term = WinTerm() 80 | term.set_console = Mock() 81 | term._back = 0 82 | 83 | term.back(5) 84 | 85 | self.assertEqual(term._back, 5) 86 | self.assertEqual(term.set_console.called, True) 87 | 88 | @skipUnless(sys.platform.startswith("win"), "requires Windows") 89 | def testStyle(self): 90 | term = WinTerm() 91 | term.set_console = Mock() 92 | term._style = 0 93 | 94 | term.style(22) 95 | 96 | self.assertEqual(term._style, 22) 97 | self.assertEqual(term.set_console.called, True) 98 | 99 | @patch('colorama.winterm.win32') 100 | def testSetConsole(self, mockWin32): 101 | mockAttr = Mock() 102 | mockAttr.wAttributes = 0 103 | mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr 104 | term = WinTerm() 105 | term.windll = Mock() 106 | 107 | term.set_console() 108 | 109 | self.assertEqual( 110 | mockWin32.SetConsoleTextAttribute.call_args, 111 | ((mockWin32.STDOUT, term.get_attrs()), {}) 112 | ) 113 | 114 | @patch('colorama.winterm.win32') 115 | def testSetConsoleOnStderr(self, mockWin32): 116 | mockAttr = Mock() 117 | mockAttr.wAttributes = 0 118 | mockWin32.GetConsoleScreenBufferInfo.return_value = mockAttr 119 | term = WinTerm() 120 | term.windll = Mock() 121 | 122 | term.set_console(on_stderr=True) 123 | 124 | self.assertEqual( 125 | mockWin32.SetConsoleTextAttribute.call_args, 126 | ((mockWin32.STDERR, term.get_attrs()), {}) 127 | ) 128 | 129 | 130 | if __name__ == '__main__': 131 | main() 132 | -------------------------------------------------------------------------------- /colorama/win32.py: -------------------------------------------------------------------------------- 1 | # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. 2 | 3 | # from winbase.h 4 | STDOUT = -11 5 | STDERR = -12 6 | 7 | ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004 8 | 9 | try: 10 | import ctypes 11 | from ctypes import LibraryLoader 12 | windll = LibraryLoader(ctypes.WinDLL) 13 | from ctypes import wintypes 14 | except (AttributeError, ImportError): 15 | windll = None 16 | SetConsoleTextAttribute = lambda *_: None 17 | winapi_test = lambda *_: None 18 | else: 19 | from ctypes import byref, Structure, c_char, POINTER 20 | 21 | COORD = wintypes._COORD 22 | 23 | class CONSOLE_SCREEN_BUFFER_INFO(Structure): 24 | """struct in wincon.h.""" 25 | _fields_ = [ 26 | ("dwSize", COORD), 27 | ("dwCursorPosition", COORD), 28 | ("wAttributes", wintypes.WORD), 29 | ("srWindow", wintypes.SMALL_RECT), 30 | ("dwMaximumWindowSize", COORD), 31 | ] 32 | def __str__(self): 33 | return '(%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d)' % ( 34 | self.dwSize.Y, self.dwSize.X 35 | , self.dwCursorPosition.Y, self.dwCursorPosition.X 36 | , self.wAttributes 37 | , self.srWindow.Top, self.srWindow.Left, self.srWindow.Bottom, self.srWindow.Right 38 | , self.dwMaximumWindowSize.Y, self.dwMaximumWindowSize.X 39 | ) 40 | 41 | _GetStdHandle = windll.kernel32.GetStdHandle 42 | _GetStdHandle.argtypes = [ 43 | wintypes.DWORD, 44 | ] 45 | _GetStdHandle.restype = wintypes.HANDLE 46 | 47 | _GetConsoleScreenBufferInfo = windll.kernel32.GetConsoleScreenBufferInfo 48 | _GetConsoleScreenBufferInfo.argtypes = [ 49 | wintypes.HANDLE, 50 | POINTER(CONSOLE_SCREEN_BUFFER_INFO), 51 | ] 52 | _GetConsoleScreenBufferInfo.restype = wintypes.BOOL 53 | 54 | _SetConsoleTextAttribute = windll.kernel32.SetConsoleTextAttribute 55 | _SetConsoleTextAttribute.argtypes = [ 56 | wintypes.HANDLE, 57 | wintypes.WORD, 58 | ] 59 | _SetConsoleTextAttribute.restype = wintypes.BOOL 60 | 61 | _SetConsoleCursorPosition = windll.kernel32.SetConsoleCursorPosition 62 | _SetConsoleCursorPosition.argtypes = [ 63 | wintypes.HANDLE, 64 | COORD, 65 | ] 66 | _SetConsoleCursorPosition.restype = wintypes.BOOL 67 | 68 | _FillConsoleOutputCharacterA = windll.kernel32.FillConsoleOutputCharacterA 69 | _FillConsoleOutputCharacterA.argtypes = [ 70 | wintypes.HANDLE, 71 | c_char, 72 | wintypes.DWORD, 73 | COORD, 74 | POINTER(wintypes.DWORD), 75 | ] 76 | _FillConsoleOutputCharacterA.restype = wintypes.BOOL 77 | 78 | _FillConsoleOutputAttribute = windll.kernel32.FillConsoleOutputAttribute 79 | _FillConsoleOutputAttribute.argtypes = [ 80 | wintypes.HANDLE, 81 | wintypes.WORD, 82 | wintypes.DWORD, 83 | COORD, 84 | POINTER(wintypes.DWORD), 85 | ] 86 | _FillConsoleOutputAttribute.restype = wintypes.BOOL 87 | 88 | _SetConsoleTitleW = windll.kernel32.SetConsoleTitleW 89 | _SetConsoleTitleW.argtypes = [ 90 | wintypes.LPCWSTR 91 | ] 92 | _SetConsoleTitleW.restype = wintypes.BOOL 93 | 94 | _GetConsoleMode = windll.kernel32.GetConsoleMode 95 | _GetConsoleMode.argtypes = [ 96 | wintypes.HANDLE, 97 | POINTER(wintypes.DWORD) 98 | ] 99 | _GetConsoleMode.restype = wintypes.BOOL 100 | 101 | _SetConsoleMode = windll.kernel32.SetConsoleMode 102 | _SetConsoleMode.argtypes = [ 103 | wintypes.HANDLE, 104 | wintypes.DWORD 105 | ] 106 | _SetConsoleMode.restype = wintypes.BOOL 107 | 108 | def _winapi_test(handle): 109 | csbi = CONSOLE_SCREEN_BUFFER_INFO() 110 | success = _GetConsoleScreenBufferInfo( 111 | handle, byref(csbi)) 112 | return bool(success) 113 | 114 | def winapi_test(): 115 | return any(_winapi_test(h) for h in 116 | (_GetStdHandle(STDOUT), _GetStdHandle(STDERR))) 117 | 118 | def GetConsoleScreenBufferInfo(stream_id=STDOUT): 119 | handle = _GetStdHandle(stream_id) 120 | csbi = CONSOLE_SCREEN_BUFFER_INFO() 121 | success = _GetConsoleScreenBufferInfo( 122 | handle, byref(csbi)) 123 | return csbi 124 | 125 | def SetConsoleTextAttribute(stream_id, attrs): 126 | handle = _GetStdHandle(stream_id) 127 | return _SetConsoleTextAttribute(handle, attrs) 128 | 129 | def SetConsoleCursorPosition(stream_id, position, adjust=True): 130 | position = COORD(*position) 131 | # If the position is out of range, do nothing. 132 | if position.Y <= 0 or position.X <= 0: 133 | return 134 | # Adjust for Windows' SetConsoleCursorPosition: 135 | # 1. being 0-based, while ANSI is 1-based. 136 | # 2. expecting (x,y), while ANSI uses (y,x). 137 | adjusted_position = COORD(position.Y - 1, position.X - 1) 138 | if adjust: 139 | # Adjust for viewport's scroll position 140 | sr = GetConsoleScreenBufferInfo(STDOUT).srWindow 141 | adjusted_position.Y += sr.Top 142 | adjusted_position.X += sr.Left 143 | # Resume normal processing 144 | handle = _GetStdHandle(stream_id) 145 | return _SetConsoleCursorPosition(handle, adjusted_position) 146 | 147 | def FillConsoleOutputCharacter(stream_id, char, length, start): 148 | handle = _GetStdHandle(stream_id) 149 | char = c_char(char.encode()) 150 | length = wintypes.DWORD(length) 151 | num_written = wintypes.DWORD(0) 152 | # Note that this is hard-coded for ANSI (vs wide) bytes. 153 | success = _FillConsoleOutputCharacterA( 154 | handle, char, length, start, byref(num_written)) 155 | return num_written.value 156 | 157 | def FillConsoleOutputAttribute(stream_id, attr, length, start): 158 | ''' FillConsoleOutputAttribute( hConsole, csbi.wAttributes, dwConSize, coordScreen, &cCharsWritten )''' 159 | handle = _GetStdHandle(stream_id) 160 | attribute = wintypes.WORD(attr) 161 | length = wintypes.DWORD(length) 162 | num_written = wintypes.DWORD(0) 163 | # Note that this is hard-coded for ANSI (vs wide) bytes. 164 | return _FillConsoleOutputAttribute( 165 | handle, attribute, length, start, byref(num_written)) 166 | 167 | def SetConsoleTitle(title): 168 | return _SetConsoleTitleW(title) 169 | 170 | def GetConsoleMode(handle): 171 | mode = wintypes.DWORD() 172 | success = _GetConsoleMode(handle, byref(mode)) 173 | if not success: 174 | raise ctypes.WinError() 175 | return mode.value 176 | 177 | def SetConsoleMode(handle, mode): 178 | success = _SetConsoleMode(handle, mode) 179 | if not success: 180 | raise ctypes.WinError() 181 | -------------------------------------------------------------------------------- /colorama/winterm.py: -------------------------------------------------------------------------------- 1 | # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. 2 | try: 3 | from msvcrt import get_osfhandle 4 | except ImportError: 5 | def get_osfhandle(_): 6 | raise OSError("This isn't windows!") 7 | 8 | 9 | from . import win32 10 | 11 | # from wincon.h 12 | class WinColor(object): 13 | BLACK = 0 14 | BLUE = 1 15 | GREEN = 2 16 | CYAN = 3 17 | RED = 4 18 | MAGENTA = 5 19 | YELLOW = 6 20 | GREY = 7 21 | 22 | # from wincon.h 23 | class WinStyle(object): 24 | NORMAL = 0x00 # dim text, dim background 25 | BRIGHT = 0x08 # bright text, dim background 26 | BRIGHT_BACKGROUND = 0x80 # dim text, bright background 27 | 28 | class WinTerm(object): 29 | 30 | def __init__(self): 31 | self._default = win32.GetConsoleScreenBufferInfo(win32.STDOUT).wAttributes 32 | self.set_attrs(self._default) 33 | self._default_fore = self._fore 34 | self._default_back = self._back 35 | self._default_style = self._style 36 | # In order to emulate LIGHT_EX in windows, we borrow the BRIGHT style. 37 | # So that LIGHT_EX colors and BRIGHT style do not clobber each other, 38 | # we track them separately, since LIGHT_EX is overwritten by Fore/Back 39 | # and BRIGHT is overwritten by Style codes. 40 | self._light = 0 41 | 42 | def get_attrs(self): 43 | return self._fore + self._back * 16 + (self._style | self._light) 44 | 45 | def set_attrs(self, value): 46 | self._fore = value & 7 47 | self._back = (value >> 4) & 7 48 | self._style = value & (WinStyle.BRIGHT | WinStyle.BRIGHT_BACKGROUND) 49 | 50 | def reset_all(self, on_stderr=None): 51 | self.set_attrs(self._default) 52 | self.set_console(attrs=self._default) 53 | self._light = 0 54 | 55 | def fore(self, fore=None, light=False, on_stderr=False): 56 | if fore is None: 57 | fore = self._default_fore 58 | self._fore = fore 59 | # Emulate LIGHT_EX with BRIGHT Style 60 | if light: 61 | self._light |= WinStyle.BRIGHT 62 | else: 63 | self._light &= ~WinStyle.BRIGHT 64 | self.set_console(on_stderr=on_stderr) 65 | 66 | def back(self, back=None, light=False, on_stderr=False): 67 | if back is None: 68 | back = self._default_back 69 | self._back = back 70 | # Emulate LIGHT_EX with BRIGHT_BACKGROUND Style 71 | if light: 72 | self._light |= WinStyle.BRIGHT_BACKGROUND 73 | else: 74 | self._light &= ~WinStyle.BRIGHT_BACKGROUND 75 | self.set_console(on_stderr=on_stderr) 76 | 77 | def style(self, style=None, on_stderr=False): 78 | if style is None: 79 | style = self._default_style 80 | self._style = style 81 | self.set_console(on_stderr=on_stderr) 82 | 83 | def set_console(self, attrs=None, on_stderr=False): 84 | if attrs is None: 85 | attrs = self.get_attrs() 86 | handle = win32.STDOUT 87 | if on_stderr: 88 | handle = win32.STDERR 89 | win32.SetConsoleTextAttribute(handle, attrs) 90 | 91 | def get_position(self, handle): 92 | position = win32.GetConsoleScreenBufferInfo(handle).dwCursorPosition 93 | # Because Windows coordinates are 0-based, 94 | # and win32.SetConsoleCursorPosition expects 1-based. 95 | position.X += 1 96 | position.Y += 1 97 | return position 98 | 99 | def set_cursor_position(self, position=None, on_stderr=False): 100 | if position is None: 101 | # I'm not currently tracking the position, so there is no default. 102 | # position = self.get_position() 103 | return 104 | handle = win32.STDOUT 105 | if on_stderr: 106 | handle = win32.STDERR 107 | win32.SetConsoleCursorPosition(handle, position) 108 | 109 | def cursor_adjust(self, x, y, on_stderr=False): 110 | handle = win32.STDOUT 111 | if on_stderr: 112 | handle = win32.STDERR 113 | position = self.get_position(handle) 114 | adjusted_position = (position.Y + y, position.X + x) 115 | win32.SetConsoleCursorPosition(handle, adjusted_position, adjust=False) 116 | 117 | def erase_screen(self, mode=0, on_stderr=False): 118 | # 0 should clear from the cursor to the end of the screen. 119 | # 1 should clear from the cursor to the beginning of the screen. 120 | # 2 should clear the entire screen, and move cursor to (1,1) 121 | handle = win32.STDOUT 122 | if on_stderr: 123 | handle = win32.STDERR 124 | csbi = win32.GetConsoleScreenBufferInfo(handle) 125 | # get the number of character cells in the current buffer 126 | cells_in_screen = csbi.dwSize.X * csbi.dwSize.Y 127 | # get number of character cells before current cursor position 128 | cells_before_cursor = csbi.dwSize.X * csbi.dwCursorPosition.Y + csbi.dwCursorPosition.X 129 | if mode == 0: 130 | from_coord = csbi.dwCursorPosition 131 | cells_to_erase = cells_in_screen - cells_before_cursor 132 | elif mode == 1: 133 | from_coord = win32.COORD(0, 0) 134 | cells_to_erase = cells_before_cursor 135 | elif mode == 2: 136 | from_coord = win32.COORD(0, 0) 137 | cells_to_erase = cells_in_screen 138 | else: 139 | # invalid mode 140 | return 141 | # fill the entire screen with blanks 142 | win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord) 143 | # now set the buffer's attributes accordingly 144 | win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord) 145 | if mode == 2: 146 | # put the cursor where needed 147 | win32.SetConsoleCursorPosition(handle, (1, 1)) 148 | 149 | def erase_line(self, mode=0, on_stderr=False): 150 | # 0 should clear from the cursor to the end of the line. 151 | # 1 should clear from the cursor to the beginning of the line. 152 | # 2 should clear the entire line. 153 | handle = win32.STDOUT 154 | if on_stderr: 155 | handle = win32.STDERR 156 | csbi = win32.GetConsoleScreenBufferInfo(handle) 157 | if mode == 0: 158 | from_coord = csbi.dwCursorPosition 159 | cells_to_erase = csbi.dwSize.X - csbi.dwCursorPosition.X 160 | elif mode == 1: 161 | from_coord = win32.COORD(0, csbi.dwCursorPosition.Y) 162 | cells_to_erase = csbi.dwCursorPosition.X 163 | elif mode == 2: 164 | from_coord = win32.COORD(0, csbi.dwCursorPosition.Y) 165 | cells_to_erase = csbi.dwSize.X 166 | else: 167 | # invalid mode 168 | return 169 | # fill the entire screen with blanks 170 | win32.FillConsoleOutputCharacter(handle, ' ', cells_to_erase, from_coord) 171 | # now set the buffer's attributes accordingly 172 | win32.FillConsoleOutputAttribute(handle, self.get_attrs(), cells_to_erase, from_coord) 173 | 174 | def set_title(self, title): 175 | win32.SetConsoleTitle(title) 176 | 177 | 178 | def enable_vt_processing(fd): 179 | if win32.windll is None or not win32.winapi_test(): 180 | return False 181 | 182 | try: 183 | handle = get_osfhandle(fd) 184 | mode = win32.GetConsoleMode(handle) 185 | win32.SetConsoleMode( 186 | handle, 187 | mode | win32.ENABLE_VIRTUAL_TERMINAL_PROCESSING, 188 | ) 189 | 190 | mode = win32.GetConsoleMode(handle) 191 | if mode & win32.ENABLE_VIRTUAL_TERMINAL_PROCESSING: 192 | return True 193 | # Can get TypeError in testsuite where 'fd' is a Mock() and IOError in python2.7 194 | except (IOError, OSError, TypeError): 195 | return False 196 | -------------------------------------------------------------------------------- /demos/demo.bat: -------------------------------------------------------------------------------- 1 | :: Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. 2 | 3 | :: Script to demonstrate features of colorama. 4 | 5 | :: This demo is also used to verify correctness visually, because we don't 6 | :: have automated tests. 7 | 8 | :: Implemented as a bash script which invokes python so that we can test the 9 | :: behaviour on exit, which resets default colors again. 10 | 11 | :: print grid of all colors and brightnesses 12 | python demo01.py 13 | 14 | :: Simple demo of changing foreground, background and brightness. 15 | python demo02.py 16 | 17 | :: Demonstrate the different behavior when autoreset is True and False. 18 | python demo03.py 19 | 20 | :: check that stripped ANSI in redirected stderr does not affect stdout 21 | if exist demo04.out del demo04.out 22 | python demo04.py 2> demo04.out 23 | type demo04.out 24 | if exist demo04.out del demo04.out 25 | 26 | :: Demonstrate the difference between colorama initialized with wrapping on and off. 27 | python demo05.py 28 | 29 | :: Demonstrate printing colored, random characters at random positions on the screen 30 | python demo06.py 31 | 32 | :: Demonstrate cursor relative movement: UP, DOWN, FORWARD, and BACK in colorama.CURSOR 33 | python demo07.py 34 | 35 | :: Demonstrate the use of a context manager instead of manually using init and deinit 36 | python demo08.py 37 | -------------------------------------------------------------------------------- /demos/demo.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. 3 | 4 | # Script to demonstrate features of colorama. 5 | 6 | # This demo is also used to verify correctness visually, because we don't have 7 | # automated tests. 8 | 9 | # Implemented as a bash script which invokes python so that we can test the 10 | # behaviour on exit, which resets default colors again. 11 | 12 | # print grid of all colors and brightnesses 13 | python demo01.py 14 | 15 | # Simple demo of changing foreground, background and brightness. 16 | python demo02.py 17 | 18 | # Demonstrate the different behavior when autoreset is True and False. 19 | python demo03.py 20 | 21 | # check that stripped ANSI in redirected stderr does not affect stdout 22 | rm -f demo04.out 23 | python demo04.py 2> demo04.out 24 | cat demo04.out 25 | rm -f demo04.out 26 | 27 | # Demonstrate the difference between colorama initialized with wrapping on and off. 28 | python demo05.py 29 | 30 | # Skip demo06 31 | # It is too visually disruptive, 32 | # making it hard to see whether any of the demos are working correctly. 33 | 34 | # Demonstrate cursor relative movement: UP, DOWN, FORWARD, and BACK in colorama.CURSOR 35 | python demo07.py 36 | 37 | # Demonstrate the use of a context manager instead of manually using init and deinit 38 | python demo08.py 39 | -------------------------------------------------------------------------------- /demos/demo01.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. 3 | 4 | # print grid of all colors and brightnesses 5 | # uses stdout.write to write chars with no newline nor spaces between them 6 | # This should run more-or-less identically on Windows and Unix. 7 | from __future__ import print_function 8 | import sys 9 | 10 | # Add parent dir to sys path, so the following 'import colorama' always finds 11 | # the local source in preference to any installed version of colorama. 12 | import fixpath 13 | from colorama import just_fix_windows_console, Fore, Back, Style 14 | 15 | just_fix_windows_console() 16 | 17 | # Fore, Back and Style are convenience classes for the constant ANSI strings that set 18 | # the foreground, background and style. The don't have any magic of their own. 19 | FORES = [ Fore.BLACK, Fore.RED, Fore.GREEN, Fore.YELLOW, Fore.BLUE, Fore.MAGENTA, Fore.CYAN, Fore.WHITE ] 20 | BACKS = [ Back.BLACK, Back.RED, Back.GREEN, Back.YELLOW, Back.BLUE, Back.MAGENTA, Back.CYAN, Back.WHITE ] 21 | STYLES = [ Style.DIM, Style.NORMAL, Style.BRIGHT ] 22 | 23 | NAMES = { 24 | Fore.BLACK: 'black', Fore.RED: 'red', Fore.GREEN: 'green', Fore.YELLOW: 'yellow', Fore.BLUE: 'blue', Fore.MAGENTA: 'magenta', Fore.CYAN: 'cyan', Fore.WHITE: 'white' 25 | , Fore.RESET: 'reset', 26 | Back.BLACK: 'black', Back.RED: 'red', Back.GREEN: 'green', Back.YELLOW: 'yellow', Back.BLUE: 'blue', Back.MAGENTA: 'magenta', Back.CYAN: 'cyan', Back.WHITE: 'white', 27 | Back.RESET: 'reset' 28 | } 29 | 30 | # show the color names 31 | sys.stdout.write(' ') 32 | for foreground in FORES: 33 | sys.stdout.write('%s%-7s' % (foreground, NAMES[foreground])) 34 | print() 35 | 36 | # make a row for each background color 37 | for background in BACKS: 38 | sys.stdout.write('%s%-7s%s %s' % (background, NAMES[background], Back.RESET, background)) 39 | # make a column for each foreground color 40 | for foreground in FORES: 41 | sys.stdout.write(foreground) 42 | # show dim, normal bright 43 | for brightness in STYLES: 44 | sys.stdout.write('%sX ' % brightness) 45 | sys.stdout.write(Style.RESET_ALL + ' ' + background) 46 | print(Style.RESET_ALL) 47 | 48 | print() 49 | -------------------------------------------------------------------------------- /demos/demo02.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. 3 | 4 | # Simple demo of changing foreground, background and brightness. 5 | 6 | from __future__ import print_function 7 | import fixpath 8 | from colorama import just_fix_windows_console, Fore, Back, Style 9 | 10 | just_fix_windows_console() 11 | 12 | print(Fore.GREEN + 'green, ' 13 | + Fore.RED + 'red, ' 14 | + Fore.RESET + 'normal, ' 15 | , end='') 16 | print(Back.GREEN + 'green, ' 17 | + Back.RED + 'red, ' 18 | + Back.RESET + 'normal, ' 19 | , end='') 20 | print(Style.DIM + 'dim, ' 21 | + Style.BRIGHT + 'bright, ' 22 | + Style.NORMAL + 'normal' 23 | , end=' ') 24 | print() 25 | -------------------------------------------------------------------------------- /demos/demo03.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. 3 | 4 | # Demonstrate the different behavior when autoreset is True and False. 5 | 6 | from __future__ import print_function 7 | import fixpath 8 | from colorama import init, Fore, Back, Style 9 | 10 | init(autoreset=True) 11 | print(Fore.CYAN + Back.MAGENTA + Style.BRIGHT + 'Line 1: colored, with autoreset=True') 12 | print('Line 2: When auto reset is True, the color settings need to be set with every print.') 13 | 14 | init(autoreset=False) 15 | print(Fore.YELLOW + Back.BLUE + Style.BRIGHT + 'Line 3: colored, with autoreset=False') 16 | print('Line 4: When autoreset=False, the prior color settings linger (this is the default behavior).') 17 | -------------------------------------------------------------------------------- /demos/demo04.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. 3 | 4 | # check that stripped ANSI in redirected stderr does not affect stdout 5 | from __future__ import print_function 6 | import sys 7 | import fixpath 8 | from colorama import init, Fore 9 | 10 | init() 11 | print(Fore.GREEN + 'GREEN set on stdout. ', end='') 12 | print(Fore.RED + 'RED redirected stderr', file=sys.stderr) 13 | print('Further stdout should be GREEN, i.e., the stderr redirection should not affect stdout.') 14 | -------------------------------------------------------------------------------- /demos/demo05.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. 3 | 4 | # Demonstrate the difference between colorama initialized with wrapping on and off. 5 | # The point of the demonstration is to show how the ANSI wrapping on Windows can be disabled. 6 | # The unwrapped cases will be interpreted with ANSI on Unix, but not on Windows. 7 | 8 | from __future__ import print_function 9 | import sys 10 | import fixpath 11 | from colorama import AnsiToWin32, init, Fore 12 | 13 | init() 14 | print('%sWrapped yellow going to stdout, via the default print function.' % Fore.YELLOW) 15 | 16 | init(wrap=False) 17 | print('%sUnwrapped CYAN going to stdout, via the default print function.' % Fore.CYAN) 18 | print('%sUnwrapped CYAN, using the file parameter to write via colorama the AnsiToWin32 function.' % Fore.CYAN, file=AnsiToWin32(sys.stdout)) 19 | print('%sUnwrapped RED going to stdout, via the default print function.' % Fore.RED) 20 | 21 | init() 22 | print('%sWrapped RED going to stdout, via the default print function.' % Fore.RED) 23 | -------------------------------------------------------------------------------- /demos/demo06.py: -------------------------------------------------------------------------------- 1 | # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. 2 | from __future__ import print_function 3 | import fixpath 4 | import colorama 5 | from colorama import Fore, Back, Style, Cursor 6 | from random import randint, choice 7 | from string import printable 8 | 9 | # Demonstrate printing colored, random characters at random positions on the screen 10 | 11 | # Fore, Back and Style are convenience classes for the constant ANSI strings that set 12 | # the foreground, background and style. They don't have any magic of their own. 13 | FORES = [ Fore.BLACK, Fore.RED, Fore.GREEN, Fore.YELLOW, Fore.BLUE, Fore.MAGENTA, Fore.CYAN, Fore.WHITE ] 14 | BACKS = [ Back.BLACK, Back.RED, Back.GREEN, Back.YELLOW, Back.BLUE, Back.MAGENTA, Back.CYAN, Back.WHITE ] 15 | STYLES = [ Style.DIM, Style.NORMAL, Style.BRIGHT ] 16 | 17 | # This assumes your terminal is 80x24. Ansi minimum coordinate is (1,1). 18 | MINY, MAXY = 1, 24 19 | MINX, MAXX = 1, 80 20 | 21 | # set of printable ASCII characters, including a space. 22 | CHARS = ' ' + printable.strip() 23 | 24 | PASSES = 1000 25 | 26 | def main(): 27 | colorama.just_fix_windows_console() 28 | pos = lambda y, x: Cursor.POS(x, y) 29 | # draw a white border. 30 | print(Back.WHITE, end='') 31 | print('%s%s' % (pos(MINY, MINX), ' '*MAXX), end='') 32 | for y in range(MINY, 1+MAXY): 33 | print('%s %s ' % (pos(y, MINX), pos(y, MAXX)), end='') 34 | print('%s%s' % (pos(MAXY, MINX), ' '*MAXX), end='') 35 | # draw some blinky lights for a while. 36 | for i in range(PASSES): 37 | print('%s%s%s%s%s' % (pos(randint(1+MINY,MAXY-1), randint(1+MINX,MAXX-1)), choice(FORES), choice(BACKS), choice(STYLES), choice(CHARS)), end='') 38 | # put cursor to top, left, and set color to white-on-black with normal brightness. 39 | print('%s%s%s%s' % (pos(MINY, MINX), Fore.WHITE, Back.BLACK, Style.NORMAL), end='') 40 | 41 | if __name__ == '__main__': 42 | main() 43 | -------------------------------------------------------------------------------- /demos/demo07.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import fixpath 3 | import colorama 4 | 5 | # Demonstrate cursor relative movement: UP, DOWN, FORWARD, and BACK in colorama.CURSOR 6 | 7 | up = colorama.Cursor.UP 8 | down = colorama.Cursor.DOWN 9 | forward = colorama.Cursor.FORWARD 10 | back = colorama.Cursor.BACK 11 | 12 | def main(): 13 | """ 14 | expected output: 15 | 1a2 16 | aba 17 | 3a4 18 | """ 19 | colorama.just_fix_windows_console() 20 | print("aaa") 21 | print("aaa") 22 | print("aaa") 23 | print(forward() + up(2) + "b" + up() + back(2) + "1" + forward() + "2" + back(3) + down(2) + "3" + forward() + "4") 24 | 25 | 26 | if __name__ == '__main__': 27 | main() 28 | -------------------------------------------------------------------------------- /demos/demo08.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | import fixpath 3 | from colorama import colorama_text, Fore 4 | 5 | 6 | def main(): 7 | """automatically reset stdout""" 8 | with colorama_text(): 9 | print(Fore.GREEN + 'text is green') 10 | print(Fore.RESET + 'text is back to normal') 11 | 12 | print('text is back to stdout') 13 | 14 | if __name__ == '__main__': 15 | main() 16 | -------------------------------------------------------------------------------- /demos/demo09.py: -------------------------------------------------------------------------------- 1 | # https://www.youtube.com/watch?v=F5a8RLY2N8M&list=PL1_riyn9sOjcKIAYzo7f8drxD-Yg9La-D&index=61 2 | # Generic colorama demo using command line arguments 3 | # By George Ogden 4 | from colorama import Fore, Back, Style, init 5 | import argparse 6 | parser = argparse.ArgumentParser("colorama demo") 7 | 8 | def format(module): 9 | return list(map(lambda x: x.lower(),module.__dict__.keys())) 10 | 11 | def find(module,item): 12 | return module.__dict__[item.upper()] 13 | 14 | parser.add_argument("-c","--colour",choices=format(Fore),default="RESET") 15 | parser.add_argument("-b","--background",choices=format(Back),default="RESET") 16 | parser.add_argument("-s","--style",choices=format(Style),default="RESET_ALL") 17 | parser.add_argument("-t","--text",default="Lorem ipsum dolor sit amet") 18 | 19 | args = parser.parse_args() 20 | 21 | print(find(Style,args.style) + find(Fore,args.colour) + find(Back,args.background) + args.text + Style.RESET_ALL) -------------------------------------------------------------------------------- /demos/fixpath.py: -------------------------------------------------------------------------------- 1 | # Copyright Jonathan Hartley 2013. BSD 3-Clause license, see LICENSE file. 2 | 3 | # Add demo dir's parent to sys path, so that 'import colorama' always finds 4 | # the local source in preference to any installed version of colorama. 5 | import sys 6 | from os.path import normpath, dirname, join 7 | local_colorama_module = normpath(join(dirname(__file__), '..')) 8 | sys.path.insert(0, local_colorama_module) 9 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [build-system] 2 | requires = [ 3 | "hatchling>=0.25.1", 4 | ] 5 | build-backend = "hatchling.build" 6 | 7 | [project] 8 | name = "colorama" 9 | description = "Cross-platform colored terminal text." 10 | readme = "README.rst" 11 | license = "BSD-3-Clause" 12 | requires-python = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.6.*" 13 | authors = [ 14 | { name = "Jonathan Hartley", email = "tartley@tartley.com" }, 15 | ] 16 | keywords = [ 17 | "ansi", 18 | "color", 19 | "colour", 20 | "crossplatform", 21 | "terminal", 22 | "text", 23 | "windows", 24 | "xplatform", 25 | ] 26 | classifiers = [ 27 | "Development Status :: 5 - Production/Stable", 28 | "Environment :: Console", 29 | "Intended Audience :: Developers", 30 | "License :: OSI Approved :: BSD License", 31 | "Operating System :: OS Independent", 32 | "Programming Language :: Python", 33 | "Programming Language :: Python :: 2", 34 | "Programming Language :: Python :: 2.7", 35 | "Programming Language :: Python :: 3", 36 | "Programming Language :: Python :: 3.7", 37 | "Programming Language :: Python :: 3.8", 38 | "Programming Language :: Python :: 3.9", 39 | "Programming Language :: Python :: 3.10", 40 | "Programming Language :: Python :: 3.11", 41 | "Programming Language :: Python :: 3.12", 42 | "Programming Language :: Python :: Implementation :: CPython", 43 | "Programming Language :: Python :: Implementation :: PyPy", 44 | "Topic :: Terminals", 45 | ] 46 | dynamic = [ 47 | "version", 48 | ] 49 | 50 | [project.urls] 51 | Homepage = "https://github.com/tartley/colorama" 52 | 53 | [tool.hatch.version] 54 | path = "colorama/__init__.py" 55 | 56 | [tool.hatch.build.targets.sdist] 57 | include = [ 58 | "/colorama", 59 | "/demos", 60 | "/CHANGELOG.rst", 61 | ] 62 | 63 | [tool.hatch.build.targets.wheel] 64 | include = [ 65 | "/colorama/*", 66 | ] 67 | -------------------------------------------------------------------------------- /release.ps1: -------------------------------------------------------------------------------- 1 | $ve="$HOME\.virtualenvs\colorama" 2 | $bin="$ve\Scripts" 3 | 4 | # Upload to PyPI. 5 | & $bin\twine.exe upload dist\colorama-*.tar.gz dist\colorama-*.whl 6 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | mock>=1.0.1;python_version<"3.3" 2 | twine>=3.1.1 3 | contextlib2;python_version<"3" 4 | build 5 | -e . 6 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # none 2 | -------------------------------------------------------------------------------- /screenshots/ubuntu-demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tartley/colorama/136808718af8b9583cb2eed1756ed6972eda4975/screenshots/ubuntu-demo.png -------------------------------------------------------------------------------- /screenshots/windows-demo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tartley/colorama/136808718af8b9583cb2eed1756ed6972eda4975/screenshots/windows-demo.png -------------------------------------------------------------------------------- /test-release: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Test the currently built release of Colorama from the dist/ dir. 4 | # Run this before making a release. 5 | # 6 | # This should be run on Windows, because Colorama is mostly a no-op elsewhere. 7 | # Hmmm, this script should probably be a .bat file then? Nah, WSL FTW. 8 | # 9 | # Uploads package from the dist/ directory to the *test* PyPI. 10 | # Create a fresh virtualenvironment and install colorama from test PyPI. 11 | # Import Colorama and make trivial use of it. 12 | 13 | # Exit on error 14 | set -eu -o pipefail 15 | 16 | syspython=python3 17 | bin="$HOME/.virtualenvs/colorama/bin" 18 | sandbox=test-release-playground 19 | 20 | # Upload to the test PyPI. 21 | $bin/twine upload --repository colorama-test dist/colorama-* \ 22 | || echo " > Expect a 400 if package was already uploaded." 23 | 24 | # cd elsewhere so we cannot import from local source. 25 | mkdir -p $sandbox 26 | ( 27 | # Create a temporary disposable virtualenv. 28 | $syspython -m venv --clear $sandbox/venv 29 | 30 | # voodoo sleep. I saw the following install fail, due to expected version 31 | # not being listed at test.pypi.org, but then a few seconds later, re-run 32 | # manually, it worked fine. 33 | sleep 5 34 | 35 | version=$(grep __version__ colorama/__init__.py | cut -d' ' -f3 | tr -d "'") 36 | 37 | cd $sandbox 38 | 39 | # Install the package we just uploaded. 40 | # (--extra-index-url for this project's requirements) 41 | venv/bin/python -m pip --quiet install --index-url https://test.pypi.org/simple --extra-index-url https://pypi.org/simple colorama==$version 42 | 43 | # Import and use Colorama from the temp virtualenv. 44 | venv/bin/python -c "import colorama; colorama.init(); print(colorama.Fore.GREEN + \"OK: Colorama\", colorama.__version__, \"from test pypi install.\")" 45 | ) 46 | 47 | # Tidy up 48 | rm -rf $sandbox 49 | -------------------------------------------------------------------------------- /test-release.ps1: -------------------------------------------------------------------------------- 1 | $syspython="python.exe" 2 | $ve="$HOME\.virtualenvs\colorama" 3 | $bin="$ve\Scripts" 4 | 5 | # Upload to the test PyPI. 6 | & $bin\twine.exe upload --repository testpypi dist\colorama-* 7 | if(!$?) { 8 | write-host " > Expect a 400 if package was already uploaded" 9 | } 10 | 11 | # cd elsewhere so we cannot import from local source. 12 | mkdir -force sandbox | out-null 13 | cd sandbox 14 | 15 | # Create a temporary disposable virtualenv. 16 | & $syspython -m venv --clear venv 17 | 18 | # TODO: What is the windows/powershell equivalent of this: 19 | # version=$(grep __version__ colorama/__init__.py | cut -d' ' -f3 | tr -d "'") 20 | 21 | # Install the package we just uploaded. 22 | # (--extra-index-url for this project's requirements) 23 | venv\Scripts\python -m pip --quiet install --index-url https://test.pypi.org/simple --extra-index-url https://pypi.org/simple colorama==$version 24 | # Import and use colorama from the temp virtualenv. 25 | venv\Scripts\python.exe -c @" 26 | import colorama; 27 | colorama.init(); 28 | print(colorama.Fore.GREEN + ""OK Colorama "" + colorama.__version__ + "" from test pypi install."") 29 | "@ 30 | 31 | cd .. 32 | -------------------------------------------------------------------------------- /test.ps1: -------------------------------------------------------------------------------- 1 | $ve="$HOME\.virtualenvs\colorama" 2 | $bin="$ve\Scripts" 3 | 4 | & $bin\python.exe -m unittest discover -p *_test.py 5 | 6 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | isolated_build = true 3 | envlist = py{27, 37, 38, 39, 310, 311, 312, py, py3} 4 | 5 | [testenv] 6 | deps = 7 | py27,pypy: mock 8 | py27,pypy: contextlib2 9 | commands = python -m unittest discover -p *_test.py 10 | --------------------------------------------------------------------------------