├── .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 |
--------------------------------------------------------------------------------