Script 'mail_helper' called by obssrc
Hello community,
here is the log from the commit of package python-termcolor for
openSUSE:Factory checked in at 2025-12-01 11:13:54
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/python-termcolor (Old)
and /work/SRC/openSUSE:Factory/.python-termcolor.new.14147 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-termcolor"
Mon Dec 1 11:13:54 2025 rev:17 rq:1320603 version:3.2.0
Changes:
--------
--- /work/SRC/openSUSE:Factory/python-termcolor/python-termcolor.changes
2024-10-08 17:25:02.262603649 +0200
+++
/work/SRC/openSUSE:Factory/.python-termcolor.new.14147/python-termcolor.changes
2025-12-01 11:14:32.821619476 +0100
@@ -1,0 +2,26 @@
+Fri Nov 28 10:55:38 UTC 2025 - John Paul Adrian Glaubitz
<[email protected]>
+
+- Update to 3.2.0
+ * Expose can_colorize as public API (#114) @hugovk
+ * Add support for Python 3.15 (#123) @hugovk
+ * Drop support for Python 3.9 (#121) @hugovk
+ * Replace action-pre-commit-uv with prek-action (#124) @hugovk
+- from version 3.1.0
+ * Add true colour RGB option as input arguments (#102) @icyveins7
+ * Cache system lookups to save invocation time (#107) @miketheman
+ * Advertise typing via classifier (#104) @miketheman
+ * Migrate coverage configuration to pyproject.toml (#105) @miketheman
+- from version 3.0.1
+ * Fix licence filename in metadata (#100) @hugovk
+- from version 3.0.0
+ * Add support for Python 3.14 (#87) @hugovk
+ * Only apply FORCE_COLOR, NO_COLOR and ANSI_COLORS_DISABLED when present
+ and not an empty string (#92) @hugovk
+ * Replace deprecated classifier with licence expression (PEP 639) (#95)
@hugovk
+ * Speedup: move typing imports into type-checking block (#94) @hugovk
+ * Lint with faster action-pre-commit-uv: 1m22s -> 48s and 21s -> 15s (#86)
@hugovk
+ * Replace literal types with strings (#97) @hugovk
+ * Remove deprecated __ALL__, use __all__ instead (#93) @hugovk
+- Update BuildRequires from pyproject.toml
+
+-------------------------------------------------------------------
Old:
----
termcolor-2.5.0.tar.gz
New:
----
termcolor-3.2.0.tar.gz
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Other differences:
------------------
++++++ python-termcolor.spec ++++++
--- /var/tmp/diff_new_pack.madva6/_old 2025-12-01 11:14:33.305639965 +0100
+++ /var/tmp/diff_new_pack.madva6/_new 2025-12-01 11:14:33.309640134 +0100
@@ -18,15 +18,15 @@
%{?sle15_python_module_pythons}
Name: python-termcolor
-Version: 2.5.0
+Version: 3.2.0
Release: 0
Summary: ANSII Color formatting for output in terminal
License: MIT
URL: https://pypi.python.org/pypi/termcolor
Source:
https://files.pythonhosted.org/packages/source/t/termcolor/termcolor-%{version}.tar.gz
-BuildRequires: %{python_module base >= 3.9}
+BuildRequires: %{python_module base >= 3.10}
BuildRequires: %{python_module hatch_vcs}
-BuildRequires: %{python_module hatchling}
+BuildRequires: %{python_module hatchling >= 1.27}
BuildRequires: %{python_module pip}
BuildRequires: %{python_module wheel}
BuildRequires: fdupes
++++++ termcolor-2.5.0.tar.gz -> termcolor-3.2.0.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/termcolor-2.5.0/.coveragerc
new/termcolor-3.2.0/.coveragerc
--- old/termcolor-2.5.0/.coveragerc 2024-10-06 21:46:09.000000000 +0200
+++ new/termcolor-3.2.0/.coveragerc 1970-01-01 01:00:00.000000000 +0100
@@ -1,11 +0,0 @@
-# .coveragerc to control coverage.py
-
-[report]
-# Regexes for lines to exclude from consideration
-exclude_also =
- # Don't complain if non-runnable code isn't run:
- if __name__ == .__main__.:
-
-[run]
-omit =
- */termcolor/__main__.py
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/termcolor-2.5.0/.github/renovate.json
new/termcolor-3.2.0/.github/renovate.json
--- old/termcolor-2.5.0/.github/renovate.json 2024-10-06 21:46:09.000000000
+0200
+++ new/termcolor-3.2.0/.github/renovate.json 2025-10-25 18:17:06.000000000
+0200
@@ -1,6 +1,6 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
- "extends": ["config:base"],
+ "extends": ["config:base", ":semanticCommitsDisabled"],
"labels": ["changelog: skip", "dependencies"],
"packageRules": [
{
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/termcolor-2.5.0/.github/workflows/deploy.yml
new/termcolor-3.2.0/.github/workflows/deploy.yml
--- old/termcolor-2.5.0/.github/workflows/deploy.yml 2024-10-06
21:46:09.000000000 +0200
+++ new/termcolor-3.2.0/.github/workflows/deploy.yml 2025-10-25
18:17:06.000000000 +0200
@@ -11,8 +11,7 @@
- published
workflow_dispatch:
-permissions:
- contents: read
+permissions: {}
env:
FORCE_COLOR: 1
@@ -24,9 +23,10 @@
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
with:
fetch-depth: 0
+ persist-credentials: false
- uses: hynek/build-and-inspect-python-package@v2
@@ -34,19 +34,18 @@
release-test-pypi:
name: Publish in-dev package to test.pypi.org
if: |
- github.repository_owner == 'termcolor'
+ github.event.repository.fork == false
&& github.event_name == 'push'
&& github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
needs: build-package
permissions:
- attestations: write
id-token: write
steps:
- name: Download packages built by build-and-inspect-python-package
- uses: actions/download-artifact@v4
+ uses: actions/download-artifact@v5
with:
name: Packages
path: dist
@@ -60,7 +59,7 @@
release-pypi:
name: Publish released package to pypi.org
if: |
- github.repository_owner == 'termcolor'
+ github.event.repository.fork == false
&& github.event.action == 'published'
runs-on: ubuntu-latest
needs: build-package
@@ -70,12 +69,10 @@
steps:
- name: Download packages built by build-and-inspect-python-package
- uses: actions/download-artifact@v4
+ uses: actions/download-artifact@v5
with:
name: Packages
path: dist
- name: Upload package to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
- with:
- attestations: true
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/termcolor-2.5.0/.github/workflows/labels.yml
new/termcolor-3.2.0/.github/workflows/labels.yml
--- old/termcolor-2.5.0/.github/workflows/labels.yml 2024-10-06
21:46:09.000000000 +0200
+++ new/termcolor-3.2.0/.github/workflows/labels.yml 2025-10-25
18:17:06.000000000 +0200
@@ -1,8 +1,5 @@
name: Sync labels
-permissions:
- pull-requests: write
-
on:
push:
branches:
@@ -13,9 +10,13 @@
jobs:
sync:
+ permissions:
+ pull-requests: write
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
+ with:
+ persist-credentials: false
- uses: micnncim/action-label-syncer@v1
with:
prune: false
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/termcolor-2.5.0/.github/workflows/lint.yml
new/termcolor-3.2.0/.github/workflows/lint.yml
--- old/termcolor-2.5.0/.github/workflows/lint.yml 2024-10-06
21:46:09.000000000 +0200
+++ new/termcolor-3.2.0/.github/workflows/lint.yml 2025-10-25
18:17:06.000000000 +0200
@@ -2,21 +2,21 @@
on: [push, pull_request, workflow_dispatch]
+permissions: {}
+
env:
FORCE_COLOR: 1
- PIP_DISABLE_PIP_VERSION_CHECK: 1
-
-permissions:
- contents: read
+ RUFF_OUTPUT_FORMAT: github
jobs:
lint:
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
- - uses: actions/setup-python@v5
+ - uses: actions/checkout@v5
+ with:
+ persist-credentials: false
+ - uses: actions/setup-python@v6
with:
python-version: "3.x"
- cache: pip
- - uses: pre-commit/[email protected]
+ - uses: j178/prek-action@v1
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/termcolor-2.5.0/.github/workflows/release-drafter.yml
new/termcolor-3.2.0/.github/workflows/release-drafter.yml
--- old/termcolor-2.5.0/.github/workflows/release-drafter.yml 2024-10-06
21:46:09.000000000 +0200
+++ new/termcolor-3.2.0/.github/workflows/release-drafter.yml 2025-10-25
18:17:06.000000000 +0200
@@ -14,12 +14,9 @@
# types: [opened, reopened, synchronize]
workflow_dispatch:
-permissions:
- contents: read
-
jobs:
update_release_draft:
- if: github.repository_owner == 'termcolor'
+ if: github.event.repository.fork == false
permissions:
# write permission is required to create a GitHub Release
contents: write
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore'
old/termcolor-2.5.0/.github/workflows/require-pr-label.yml
new/termcolor-3.2.0/.github/workflows/require-pr-label.yml
--- old/termcolor-2.5.0/.github/workflows/require-pr-label.yml 2024-10-06
21:46:09.000000000 +0200
+++ new/termcolor-3.2.0/.github/workflows/require-pr-label.yml 2025-10-25
18:17:06.000000000 +0200
@@ -17,6 +17,11 @@
with:
mode: minimum
count: 1
- labels:
- "changelog: Added, changelog: Changed, changelog: Deprecated,
changelog:
- Fixed, changelog: Removed, changelog: Security, changelog: skip"
+ labels: |
+ changelog: Added
+ changelog: Changed
+ changelog: Deprecated
+ changelog: Fixed
+ changelog: Removed
+ changelog: Security
+ changelog: skip
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/termcolor-2.5.0/.github/workflows/test.yml
new/termcolor-3.2.0/.github/workflows/test.yml
--- old/termcolor-2.5.0/.github/workflows/test.yml 2024-10-06
21:46:09.000000000 +0200
+++ new/termcolor-3.2.0/.github/workflows/test.yml 2025-10-25
18:17:06.000000000 +0200
@@ -2,11 +2,11 @@
on: [push, pull_request, workflow_dispatch]
-permissions:
- contents: read
+permissions: {}
env:
FORCE_COLOR: 1
+ PIP_DISABLE_PIP_VERSION_CHECK: 1
jobs:
test:
@@ -14,27 +14,44 @@
strategy:
fail-fast: false
matrix:
- python-version: ["pypy3.10", "3.9", "3.10", "3.11", "3.12", "3.13"]
+ python-version:
+ - "pypy3.11"
+ - "3.15t"
+ - "3.15"
+ - "3.14t"
+ - "3.14"
+ - "3.13t"
+ - "3.13"
+ - "3.12"
+ - "3.11"
+ - "3.10"
os: [windows-latest, macos-latest, ubuntu-latest]
steps:
- - uses: actions/checkout@v4
+ - uses: actions/checkout@v5
+ with:
+ persist-credentials: false
- name: Set up Python ${{ matrix.python-version }}
- uses: actions/setup-python@v5
+ uses: actions/setup-python@v6
with:
python-version: ${{ matrix.python-version }}
allow-prereleases: true
+ - name: Set PYTHON_GIL
+ if: endsWith(matrix.python-version, 't')
+ run: |
+ echo "PYTHON_GIL=0" >> "$GITHUB_ENV"
+
- name: Install uv
- uses: hynek/setup-cached-uv@v2
+ uses: astral-sh/setup-uv@v6
- name: Tox tests
run: |
- uvx --with tox-uv tox -e py
+ uvx --python ${{ matrix.python-version }} --with tox-uv tox -e py
- name: Upload coverage
- uses: codecov/codecov-action@v4
+ uses: codecov/codecov-action@v5
with:
flags: ${{ matrix.os }}
name: ${{ matrix.os }} Python ${{ matrix.python-version }}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/termcolor-2.5.0/.github/zizmor.yml
new/termcolor-3.2.0/.github/zizmor.yml
--- old/termcolor-2.5.0/.github/zizmor.yml 1970-01-01 01:00:00.000000000
+0100
+++ new/termcolor-3.2.0/.github/zizmor.yml 2025-10-25 18:17:06.000000000
+0200
@@ -0,0 +1,7 @@
+# Configuration for the zizmor static analysis tool, run via pre-commit in CI
+# https://woodruffw.github.io/zizmor/configuration/
+rules:
+ unpinned-uses:
+ config:
+ policies:
+ "*": ref-pin
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/termcolor-2.5.0/.gitignore
new/termcolor-3.2.0/.gitignore
--- old/termcolor-2.5.0/.gitignore 2024-10-06 21:46:09.000000000 +0200
+++ new/termcolor-3.2.0/.gitignore 2025-10-25 18:17:06.000000000 +0200
@@ -136,3 +136,6 @@
# Cython debug symbols
cython_debug/
+
+# Mutation Testing artifacts
+mutants/
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/termcolor-2.5.0/.pre-commit-config.yaml
new/termcolor-3.2.0/.pre-commit-config.yaml
--- old/termcolor-2.5.0/.pre-commit-config.yaml 2024-10-06 21:46:09.000000000
+0200
+++ new/termcolor-3.2.0/.pre-commit-config.yaml 2025-10-25 18:17:06.000000000
+0200
@@ -1,17 +1,17 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
- rev: v0.6.9
+ rev: v0.13.3
hooks:
- - id: ruff
+ - id: ruff-check
args: [--exit-non-zero-on-fix]
- repo: https://github.com/psf/black-pre-commit-mirror
- rev: 24.8.0
+ rev: 25.9.0
hooks:
- id: black
- repo: https://github.com/pre-commit/pre-commit-hooks
- rev: v5.0.0
+ rev: v6.0.0
hooks:
- id: check-added-large-files
- id: check-case-conflict
@@ -25,43 +25,54 @@
- id: trailing-whitespace
- repo: https://github.com/python-jsonschema/check-jsonschema
- rev: 0.29.3
+ rev: 0.34.0
hooks:
- id: check-github-workflows
- id: check-renovate
- repo: https://github.com/rhysd/actionlint
- rev: v1.7.3
+ rev: v1.7.7
hooks:
- id: actionlint
+ - repo: https://github.com/woodruffw/zizmor-pre-commit
+ rev: v1.14.2
+ hooks:
+ - id: zizmor
+
- repo: https://github.com/pre-commit/mirrors-mypy
- rev: v1.11.2
+ rev: v1.18.2
hooks:
- id: mypy
args: [--strict, --pretty, --show-error-codes]
additional_dependencies: [pytest]
- repo: https://github.com/tox-dev/pyproject-fmt
- rev: 2.2.4
+ rev: v2.7.0
hooks:
- id: pyproject-fmt
- repo: https://github.com/abravalheri/validate-pyproject
- rev: v0.20.2
+ rev: v0.24.1
hooks:
- id: validate-pyproject
- repo: https://github.com/tox-dev/tox-ini-fmt
- rev: 1.4.1
+ rev: 1.6.0
hooks:
- id: tox-ini-fmt
+ - repo: https://github.com/google/yamlfmt
+ rev: v0.17.2
+ hooks:
+ - id: yamlfmt
+
- repo: https://github.com/rbubley/mirrors-prettier
- rev: v3.3.3
+ rev: v3.6.2
hooks:
- id: prettier
args: [--prose-wrap=always, --print-width=88]
+ exclude_types: [yaml]
- repo: meta
hooks:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/termcolor-2.5.0/.yamlfmt.yaml
new/termcolor-3.2.0/.yamlfmt.yaml
--- old/termcolor-2.5.0/.yamlfmt.yaml 1970-01-01 01:00:00.000000000 +0100
+++ new/termcolor-3.2.0/.yamlfmt.yaml 2025-10-25 18:17:06.000000000 +0200
@@ -0,0 +1,2 @@
+formatter:
+ retain_line_breaks_single: true
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/termcolor-2.5.0/PKG-INFO new/termcolor-3.2.0/PKG-INFO
--- old/termcolor-2.5.0/PKG-INFO 2024-10-06 21:46:09.000000000 +0200
+++ new/termcolor-3.2.0/PKG-INFO 2025-10-25 18:17:06.000000000 +0200
@@ -1,31 +1,32 @@
-Metadata-Version: 2.3
+Metadata-Version: 2.4
Name: termcolor
-Version: 2.5.0
+Version: 3.2.0
Summary: ANSI color formatting for output in terminal
Project-URL: Changelog, https://github.com/termcolor/termcolor/releases
Project-URL: Homepage, https://github.com/termcolor/termcolor
Project-URL: Source, https://github.com/termcolor/termcolor
Author-email: Konstantin Lepa <[email protected]>
Maintainer: Hugo van Kemenade
-License: MIT
+License-Expression: MIT
License-File: COPYING.txt
Keywords: ANSI,ANSI color,ANSI
colour,color,colour,formatting,termcolor,terminal
Classifier: Development Status :: 5 - Production/Stable
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
-Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3 :: Only
-Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
+Classifier: Programming Language :: Python :: 3.14
+Classifier: Programming Language :: Python :: 3.15
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Classifier: Topic :: Terminals
-Requires-Python: >=3.9
+Classifier: Typing :: Typed
+Requires-Python: >=3.10
Provides-Extra: tests
Requires-Dist: pytest; extra == 'tests'
Requires-Dist: pytest-cov; extra == 'tests'
@@ -85,6 +86,9 @@
cprint(i, "magenta", end=" ")
cprint("Attention!", "red", attrs=["bold"], file=sys.stderr)
+
+# You can also specify 0-255 RGB ints via a tuple
+cprint("Both foreground and background can use tuples", (100, 150, 250), (50,
60, 70))
```
## Text properties
@@ -108,6 +112,9 @@
| `light_magenta` | `on_light_magenta` | |
| `light_cyan` | `on_light_cyan` | |
+You can also use any arbitrary RGB color specified as a tuple of 0-255
integers, for
+example, `(100, 150, 250)`.
+
## Terminal properties
| Terminal | bold | dark | underline | blink | reverse | concealed
|
@@ -131,11 +138,12 @@
1. Calling `colored` or `cprint` with a truthy `no_color` disables colour.
2. Calling `colored` or `cprint` with a truthy `force_color` forces colour.
-3. Setting the `ANSI_COLORS_DISABLED` environment variable to any value
disables colour.
-4. Setting the [`NO_COLOR`](https://no-color.org/) environment variable to any
value
+3. Setting the `ANSI_COLORS_DISABLED` environment variable to any non-empty
value
disables colour.
+4. Setting the [`NO_COLOR`](https://no-color.org/) environment variable to any
non-empty
+ value disables colour.
5. Setting the [`FORCE_COLOR`](https://force-color.org/) environment variable
to any
- value forces colour.
+ non-empty value forces colour.
6. Setting the `TERM` environment variable to `dumb`, or using such a
[dumb
terminal](https://en.wikipedia.org/wiki/Computer_terminal#Character-oriented_terminal),
disables colour.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/termcolor-2.5.0/README.md
new/termcolor-3.2.0/README.md
--- old/termcolor-2.5.0/README.md 2024-10-06 21:46:09.000000000 +0200
+++ new/termcolor-3.2.0/README.md 2025-10-25 18:17:06.000000000 +0200
@@ -52,6 +52,9 @@
cprint(i, "magenta", end=" ")
cprint("Attention!", "red", attrs=["bold"], file=sys.stderr)
+
+# You can also specify 0-255 RGB ints via a tuple
+cprint("Both foreground and background can use tuples", (100, 150, 250), (50,
60, 70))
```
## Text properties
@@ -75,6 +78,9 @@
| `light_magenta` | `on_light_magenta` | |
| `light_cyan` | `on_light_cyan` | |
+You can also use any arbitrary RGB color specified as a tuple of 0-255
integers, for
+example, `(100, 150, 250)`.
+
## Terminal properties
| Terminal | bold | dark | underline | blink | reverse | concealed
|
@@ -98,11 +104,12 @@
1. Calling `colored` or `cprint` with a truthy `no_color` disables colour.
2. Calling `colored` or `cprint` with a truthy `force_color` forces colour.
-3. Setting the `ANSI_COLORS_DISABLED` environment variable to any value
disables colour.
-4. Setting the [`NO_COLOR`](https://no-color.org/) environment variable to any
value
+3. Setting the `ANSI_COLORS_DISABLED` environment variable to any non-empty
value
disables colour.
+4. Setting the [`NO_COLOR`](https://no-color.org/) environment variable to any
non-empty
+ value disables colour.
5. Setting the [`FORCE_COLOR`](https://force-color.org/) environment variable
to any
- value forces colour.
+ non-empty value forces colour.
6. Setting the `TERM` environment variable to `dumb`, or using such a
[dumb
terminal](https://en.wikipedia.org/wiki/Computer_terminal#Character-oriented_terminal),
disables colour.
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/termcolor-2.5.0/RELEASING.md
new/termcolor-3.2.0/RELEASING.md
--- old/termcolor-2.5.0/RELEASING.md 2024-10-06 21:46:09.000000000 +0200
+++ new/termcolor-3.2.0/RELEASING.md 2025-10-25 18:17:06.000000000 +0200
@@ -1,4 +1,4 @@
-# Release Checklist
+# Release checklist
- [ ] Get `main` to the appropriate code release state.
[GitHub Actions](https://github.com/termcolor/termcolor/actions) should
be running
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/termcolor-2.5.0/pyproject.toml
new/termcolor-3.2.0/pyproject.toml
--- old/termcolor-2.5.0/pyproject.toml 2024-10-06 21:46:09.000000000 +0200
+++ new/termcolor-3.2.0/pyproject.toml 2025-10-25 18:17:06.000000000 +0200
@@ -2,7 +2,7 @@
build-backend = "hatchling.build"
requires = [
"hatch-vcs",
- "hatchling",
+ "hatchling>=1.27",
]
[project]
@@ -19,26 +19,28 @@
"termcolor",
"terminal",
]
-license = { text = "MIT" }
+license = "MIT"
+license-files = [ "COPYING.txt" ]
maintainers = [ { name = "Hugo van Kemenade" } ]
authors = [ { name = "Konstantin Lepa", email = "[email protected]" } ]
-requires-python = ">=3.9"
+requires-python = ">=3.10"
classifiers = [
"Development Status :: 5 - Production/Stable",
"Environment :: Console",
"Intended Audience :: Developers",
- "License :: OSI Approved :: MIT License",
"Operating System :: OS Independent",
"Programming Language :: Python",
"Programming Language :: Python :: 3 :: Only",
- "Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
+ "Programming Language :: Python :: 3.14",
+ "Programming Language :: Python :: 3.15",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
"Topic :: Terminals",
+ "Typing :: Typed",
]
dynamic = [ "version" ]
optional-dependencies.tests = [
@@ -57,6 +59,7 @@
[tool.ruff]
fix = true
+
lint.select = [
"C4", # flake8-comprehensions
"E", # pycodestyle errors
@@ -67,6 +70,8 @@
"ISC", # flake8-implicit-str-concat
"LOG", # flake8-logging
"PGH", # pygrep-hooks
+ "PIE", # flake8-pie
+ "PT", # flake8-pytest-style
"PYI", # flake8-pyi
"RUF022", # unsorted-dunder-all
"RUF100", # unused noqa (yesqa)
@@ -75,23 +80,37 @@
"YTT", # flake8-2020
]
lint.ignore = [
- "E203", # Whitespace before ':'
- "E221", # Multiple spaces before operator
- "E226", # Missing whitespace around arithmetic operator
- "E241", # Multiple spaces after ','
- "UP038", # Makes code slower and more verbose
+ "E203", # Whitespace before ':'
+ "E221", # Multiple spaces before operator
+ "E226", # Missing whitespace around arithmetic operator
+ "E241", # Multiple spaces after ','
+ "PIE790", # flake8-pie: unnecessary-placeholder
+ "UP038", # Makes code slower and more verbose
]
lint.flake8-import-conventions.aliases.datetime = "dt"
lint.flake8-import-conventions.banned-from = [ "datetime" ]
+lint.flake8-pytest-style.parametrize-names-type = "csv"
lint.isort.known-first-party = [ "termcolor" ]
lint.isort.required-imports = [ "from __future__ import annotations" ]
[tool.pyproject-fmt]
-max_supported_python = "3.13"
+max_supported_python = "3.15"
[tool.pytest.ini_options]
-filterwarnings = [
- # Python <= 3.11
- "ignore:sys.monitoring isn't available, using default
core:coverage.exceptions.CoverageWarning",
-]
testpaths = [ "tests" ]
+
+[tool.coverage.run]
+branch = true
+omit = [
+ "*/termcolor/__main__.py",
+]
+
+[tool.coverage.html]
+show_contexts = true
+
+[tool.coverage.report]
+# Regexes for lines to exclude from consideration
+exclude_also = [
+ # Don't complain if non-runnable code isn't run:
+ "if __name__ == .__main__.:",
+]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/termcolor-2.5.0/src/termcolor/__init__.py
new/termcolor-3.2.0/src/termcolor/__init__.py
--- old/termcolor-2.5.0/src/termcolor/__init__.py 2024-10-06
21:46:09.000000000 +0200
+++ new/termcolor-3.2.0/src/termcolor/__init__.py 2025-10-25
18:17:06.000000000 +0200
@@ -2,13 +2,22 @@
from __future__ import annotations
-from termcolor.termcolor import ATTRIBUTES, COLORS, HIGHLIGHTS, RESET,
colored, cprint
+from termcolor.termcolor import (
+ ATTRIBUTES,
+ COLORS,
+ HIGHLIGHTS,
+ RESET,
+ can_colorize,
+ colored,
+ cprint,
+)
__all__ = [
"ATTRIBUTES",
"COLORS",
"HIGHLIGHTS",
"RESET",
+ "can_colorize",
"colored",
"cprint",
]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/termcolor-2.5.0/src/termcolor/__main__.py
new/termcolor-3.2.0/src/termcolor/__main__.py
--- old/termcolor-2.5.0/src/termcolor/__main__.py 2024-10-06
21:46:09.000000000 +0200
+++ new/termcolor-3.2.0/src/termcolor/__main__.py 2025-10-25
18:17:06.000000000 +0200
@@ -67,3 +67,20 @@
print("Test mixing:")
cprint("Underline red on black color", "red", "on_black", ["underline"])
cprint("Reversed green on red color", "green", "on_red", ["reverse"])
+ print("-" * 78)
+
+ print("Test RGB:")
+ cprint("Pure red text (255, 0, 0)", (255, 0, 0))
+ cprint("Default red for comparison", "red")
+ cprint("Pure green text (0, 0, 0)", (0, 255, 0))
+ cprint("Default green for comparison", "green")
+ cprint("Pure blue text (0, 0, 0)", (0, 0, 255))
+ cprint("Default blue for comparison", "blue")
+ cprint("Pure yellow text (255, 255, 0)", (255, 255, 0))
+ cprint("Default yellow for comparison", "yellow")
+ cprint("Pure cyan text (0, 255, 255)", (0, 255, 255))
+ cprint("Default cyan for comparison", "cyan")
+ cprint("Pure magenta text (255, 0, 255)", (255, 0, 255))
+ cprint("Default magenta for comparison", "magenta")
+ cprint("Light pink (255, 182, 193)", (255, 182, 193))
+ cprint("Light pink (255, 105, 180)", (255, 105, 180))
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/termcolor-2.5.0/src/termcolor/_types.py
new/termcolor-3.2.0/src/termcolor/_types.py
--- old/termcolor-2.5.0/src/termcolor/_types.py 2024-10-06 21:46:09.000000000
+0200
+++ new/termcolor-3.2.0/src/termcolor/_types.py 1970-01-01 01:00:00.000000000
+0100
@@ -1,53 +0,0 @@
-from __future__ import annotations
-
-from typing import Literal
-
-Attribute = Literal[
- "bold",
- "dark",
- "underline",
- "blink",
- "reverse",
- "concealed",
- "strike",
-]
-
-Highlight = Literal[
- "on_black",
- "on_grey",
- "on_red",
- "on_green",
- "on_yellow",
- "on_blue",
- "on_magenta",
- "on_cyan",
- "on_light_grey",
- "on_dark_grey",
- "on_light_red",
- "on_light_green",
- "on_light_yellow",
- "on_light_blue",
- "on_light_magenta",
- "on_light_cyan",
- "on_white",
-]
-
-Color = Literal[
- "black",
- "grey",
- "red",
- "green",
- "yellow",
- "blue",
- "magenta",
- "cyan",
- "light_grey",
- "dark_grey",
- "light_red",
- "light_green",
- "light_yellow",
- "light_blue",
- "light_magenta",
- "light_cyan",
- "white",
-]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/termcolor-2.5.0/src/termcolor/termcolor.py
new/termcolor-3.2.0/src/termcolor/termcolor.py
--- old/termcolor-2.5.0/src/termcolor/termcolor.py 2024-10-06
21:46:09.000000000 +0200
+++ new/termcolor-3.2.0/src/termcolor/termcolor.py 2025-10-25
18:17:06.000000000 +0200
@@ -27,27 +27,15 @@
import io
import os
import sys
-import warnings
-from collections.abc import Iterable
-from typing import Any
-
-from ._types import Attribute, Color, Highlight
-
-
-def __getattr__(name: str) -> list[str]:
- if name == "__ALL__":
- warnings.warn(
- "__ALL__ is deprecated and will be removed in termcolor 3. "
- "Use __all__ instead.",
- DeprecationWarning,
- stacklevel=2,
- )
- return ["colored", "cprint"]
- msg = f"module '{__name__}' has no attribute '{name}'"
- raise AttributeError(msg)
+from functools import cache
+TYPE_CHECKING = False
+if TYPE_CHECKING:
+ from collections.abc import Iterable
+ from typing import Any
-ATTRIBUTES: dict[Attribute, int] = {
+
+ATTRIBUTES: dict[str, int] = {
"bold": 1,
"dark": 2,
"underline": 4,
@@ -57,7 +45,7 @@
"strike": 9,
}
-HIGHLIGHTS: dict[Highlight, int] = {
+HIGHLIGHTS: dict[str, int] = {
"on_black": 40,
"on_grey": 40, # Actually black but kept for backwards compatibility
"on_red": 41,
@@ -77,7 +65,7 @@
"on_white": 107,
}
-COLORS: dict[Color, int] = {
+COLORS: dict[str, int] = {
"black": 30,
"grey": 30, # Actually black but kept for backwards compatibility
"red": 31,
@@ -101,7 +89,8 @@
RESET = "\033[0m"
-def _can_do_colour(
+@cache
+def can_colorize(
*, no_color: bool | None = None, force_color: bool | None = None
) -> bool:
"""Check env vars and for tty/dumb terminal"""
@@ -117,11 +106,11 @@
return True
# Then check env vars:
- if "ANSI_COLORS_DISABLED" in os.environ:
+ if os.environ.get("ANSI_COLORS_DISABLED"):
return False
- if "NO_COLOR" in os.environ:
+ if os.environ.get("NO_COLOR"):
return False
- if "FORCE_COLOR" in os.environ:
+ if os.environ.get("FORCE_COLOR"):
return True
# Then check system:
@@ -138,9 +127,9 @@
def colored(
text: object,
- color: Color | None = None,
- on_color: Highlight | None = None,
- attrs: Iterable[Attribute] | None = None,
+ color: str | tuple[int, int, int] | None = None,
+ on_color: str | tuple[int, int, int] | None = None,
+ attrs: Iterable[str] | None = None,
*,
no_color: bool | None = None,
force_color: bool | None = None,
@@ -157,23 +146,35 @@
on_light_grey, on_dark_grey, on_light_red, on_light_green,
on_light_yellow,
on_light_blue, on_light_magenta, on_light_cyan.
+ Alternatively, both text colors (color) and highlights (on_color) may
+ be specified via a tuple of 0-255 ints (R, G, B).
+
Available attributes:
bold, dark, underline, blink, reverse, concealed.
Example:
colored('Hello, World!', 'red', 'on_black', ['bold', 'blink'])
colored('Hello, World!', 'green')
+ colored('Hello, World!', (255, 0, 255)) # Purple
"""
result = str(text)
- if not _can_do_colour(no_color=no_color, force_color=force_color):
+ if not can_colorize(no_color=no_color, force_color=force_color):
return result
fmt_str = "\033[%dm%s"
+ rgb_fore_fmt_str = "\033[38;2;%d;%d;%dm%s"
+ rgb_back_fmt_str = "\033[48;2;%d;%d;%dm%s"
if color is not None:
- result = fmt_str % (COLORS[color], result)
+ if isinstance(color, str):
+ result = fmt_str % (COLORS[color], result)
+ elif isinstance(color, tuple):
+ result = rgb_fore_fmt_str % (color[0], color[1], color[2], result)
if on_color is not None:
- result = fmt_str % (HIGHLIGHTS[on_color], result)
+ if isinstance(on_color, str):
+ result = fmt_str % (HIGHLIGHTS[on_color], result)
+ elif isinstance(on_color, tuple):
+ result = rgb_back_fmt_str % (on_color[0], on_color[1],
on_color[2], result)
if attrs is not None:
for attr in attrs:
@@ -186,9 +187,9 @@
def cprint(
text: object,
- color: Color | None = None,
- on_color: Highlight | None = None,
- attrs: Iterable[Attribute] | None = None,
+ color: str | tuple[int, int, int] | None = None,
+ on_color: str | tuple[int, int, int] | None = None,
+ attrs: Iterable[str] | None = None,
*,
no_color: bool | None = None,
force_color: bool | None = None,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/termcolor-2.5.0/tests/test_termcolor.py
new/termcolor-3.2.0/tests/test_termcolor.py
--- old/termcolor-2.5.0/tests/test_termcolor.py 2024-10-06 21:46:09.000000000
+0200
+++ new/termcolor-3.2.0/tests/test_termcolor.py 2025-10-25 18:17:06.000000000
+0200
@@ -1,13 +1,17 @@
from __future__ import annotations
+import io
import os
-from collections.abc import Iterable
-from typing import Any
import pytest
from termcolor import ATTRIBUTES, COLORS, HIGHLIGHTS, colored, cprint,
termcolor
-from termcolor._types import Attribute, Color, Highlight
+
+TYPE_CHECKING = False
+if TYPE_CHECKING:
+ from collections.abc import Generator, Iterable
+ from typing import Any
+
ALL_COLORS = [*COLORS, None]
ALL_HIGHLIGHTS = [*HIGHLIGHTS, None]
@@ -23,6 +27,17 @@
pass
[email protected](autouse=True)
+def clear_lru_cache() -> Generator[Any, None, None]:
+ """
+ Tests may override the underpinnings of the system-under-test,
+ clear the cache after each test to ensure a clean slate.
+ """
+ yield
+ # Clear the cache after each test
+ termcolor.can_colorize.cache_clear()
+
+
def test_basic(monkeypatch: pytest.MonkeyPatch) -> None:
# Arrange
monkeypatch.setattr(os, "isatty", lambda fd: True)
@@ -50,9 +65,9 @@
capsys: pytest.CaptureFixture[str],
expected: str,
text: str,
- color: Color | None = None,
- on_color: Highlight | None = None,
- attrs: Iterable[Attribute] | None = None,
+ color: str | tuple[int, int, int] | None = None,
+ on_color: str | tuple[int, int, int] | None = None,
+ attrs: Iterable[str] | None = None,
**kwargs: Any,
) -> None:
cprint(text, color, on_color, attrs, **kwargs)
@@ -76,12 +91,15 @@
("light_grey", "\x1b[37mtext\x1b[0m"),
("dark_grey", "\x1b[90mtext\x1b[0m"),
("light_blue", "\x1b[94mtext\x1b[0m"),
+ ((1, 2, 3), "\x1b[38;2;1;2;3mtext\x1b[0m"),
+ ((100, 200, 150), "\x1b[38;2;100;200;150mtext\x1b[0m"),
+ (000, "text\x1b[0m"), # invalid input type
],
)
def test_color(
capsys: pytest.CaptureFixture[str],
monkeypatch: pytest.MonkeyPatch,
- color: Color,
+ color: str | tuple[int, int, int],
expected: str,
) -> None:
# Arrange
@@ -109,12 +127,15 @@
("on_light_grey", "\x1b[47mtext\x1b[0m"),
("on_dark_grey", "\x1b[100mtext\x1b[0m"),
("on_light_blue", "\x1b[104mtext\x1b[0m"),
+ ((1, 2, 3), "\x1b[48;2;1;2;3mtext\x1b[0m"),
+ ((100, 200, 150), "\x1b[48;2;100;200;150mtext\x1b[0m"),
+ (000, "text\x1b[0m"), # invalid input type
],
)
def test_on_color(
capsys: pytest.CaptureFixture[str],
monkeypatch: pytest.MonkeyPatch,
- on_color: Highlight,
+ on_color: str | tuple[int, int, int],
expected: str,
) -> None:
# Arrange
@@ -142,7 +163,7 @@
def test_attrs(
capsys: pytest.CaptureFixture[str],
monkeypatch: pytest.MonkeyPatch,
- attr: Attribute,
+ attr: str,
expected: str,
) -> None:
# Arrange
@@ -187,26 +208,40 @@
"false",
"1",
"0",
- "",
],
)
def test_environment_variables_force_color(
monkeypatch: pytest.MonkeyPatch, test_value: str
) -> None:
- """Assert color applied when this env var set, regardless of value"""
+ """Assert color applied when this env var is present and not an empty
string,
+ regardless of value"""
monkeypatch.setenv("FORCE_COLOR", test_value)
assert colored("text", color="cyan") == "\x1b[36mtext\x1b[0m"
+def test_environment_variables_force_color_empty_string(
+ monkeypatch: pytest.MonkeyPatch,
+) -> None:
+ """Assert color not applied when empty string"""
+ monkeypatch.setenv("FORCE_COLOR", "")
+ assert colored("text", color="cyan") == "text"
+
+
@pytest.mark.parametrize(
"test_no_color, test_force_color, test_env_vars, expected",
[
# Set only env vars
(None, None, ["ANSI_COLORS_DISABLED=1"], False),
+ (None, None, ["ANSI_COLORS_DISABLED="], False),
(None, None, ["NO_COLOR=1"], False),
+ (None, None, ["NO_COLOR="], False),
(None, None, ["FORCE_COLOR=1"], True),
+ (None, None, ["FORCE_COLOR="], False),
(None, None, ["ANSI_COLORS_DISABLED=1", "NO_COLOR=1",
"FORCE_COLOR=1"], False),
+ (None, None, ["ANSI_COLORS_DISABLED=1", "FORCE_COLOR=1"], False),
+ (None, None, ["ANSI_COLORS_DISABLED=", "FORCE_COLOR=1"], True),
(None, None, ["NO_COLOR=1", "FORCE_COLOR=1"], False),
+ (None, None, ["NO_COLOR=1", "FORCE_COLOR="], False),
(None, None, ["TERM=dumb"], False),
# Set only parameter overrides
(True, None, [None], False),
@@ -237,11 +272,40 @@
monkeypatch.setenv(name, value)
assert (
- termcolor._can_do_colour(no_color=test_no_color,
force_color=test_force_color)
+ termcolor.can_colorize(no_color=test_no_color,
force_color=test_force_color)
== expected
)
+def test_cached_behavior(monkeypatch: pytest.MonkeyPatch) -> None:
+ """Assert that the cached behavior is correct"""
+ # Arrange
+ monkeypatch.setattr(os, "isatty", lambda fd: True)
+ monkeypatch.setattr("sys.stdout.isatty", lambda: True)
+ monkeypatch.setattr("sys.stdout.fileno", lambda: 1)
+
+ # Act / Assert
+ assert colored("text") == "text\x1b[0m"
+ assert colored("text") == "text\x1b[0m"
+ assert termcolor.can_colorize.cache_info().hits == 1
+ assert termcolor.can_colorize.cache_info().misses == 1
+ assert termcolor.can_colorize.cache_info().currsize == 1
+
+ # Different function signature passed to underlying function, adds to cache
+ assert colored("text", force_color=True) == "text\x1b[0m"
+ assert colored("text", no_color=True) == "text"
+ assert termcolor.can_colorize.cache_info().hits == 1
+ assert termcolor.can_colorize.cache_info().misses == 3
+ assert termcolor.can_colorize.cache_info().currsize == 3
+
+ # Changing text or color should not add to cache
+ assert colored("text", color="red") == "\x1b[31mtext\x1b[0m"
+ assert colored("other", color="blue") == "\x1b[34mother\x1b[0m"
+ assert termcolor.can_colorize.cache_info().hits == 3
+ assert termcolor.can_colorize.cache_info().misses == 3
+ assert termcolor.can_colorize.cache_info().currsize == 3
+
+
@pytest.mark.parametrize(
"test_isatty, expected",
[
@@ -260,7 +324,64 @@
assert colored("text", color="cyan") == expected
-def test_all_deprecation() -> None:
- """Assert that __ALL__ is deprecated (use __all__ instead)"""
- with pytest.deprecated_call():
- assert termcolor.__ALL__
+def test_no_sys_stdout_fileno(monkeypatch: pytest.MonkeyPatch) -> None:
+ """Assert no color when sys.stdout has no file descriptor"""
+ # Arrange
+ monkeypatch.setattr("sys.stdout", object())
+
+ # Act / Assert
+ assert colored("text", color="cyan") == "text"
+
+
[email protected](
+ "test_isatty, expected",
+ [
+ (True, "\x1b[36mtext\x1b[0m"),
+ (False, "text"),
+ ],
+)
+def test_unsupported_operation(
+ monkeypatch: pytest.MonkeyPatch, test_isatty: bool, expected: str
+) -> None:
+ """Assert no color when sys.stdout does not support the operation"""
+
+ # Arrange
+ class MockStdout:
+ def fileno(self) -> None:
+ raise io.UnsupportedOperation()
+
+ def isatty(self) -> bool:
+ return test_isatty
+
+ monkeypatch.setattr("sys.stdout", MockStdout())
+
+ # Act / Assert
+ assert colored("text", color="cyan") == expected
+
+
[email protected](
+ "isatty_value, expected, kwargs",
+ [
+ # no_color when isatty
+ (True, "text", {"no_color": True}),
+ # force_color when not isatty
+ (False, "\x1b[36mtext\x1b[0m", {"force_color": True}),
+ # print kwargs pass through
+ (True, "\x1b[36mtext\x1b[0m!", {"end": "!\n"}),
+ ],
+)
+def test_cprint_kwargs(
+ capsys: pytest.CaptureFixture[str],
+ monkeypatch: pytest.MonkeyPatch,
+ isatty_value: bool,
+ expected: str,
+ kwargs: dict[str, Any],
+) -> None:
+ """Test cprint with various parameter combinations"""
+ # Arrange
+ monkeypatch.setattr(os, "isatty", lambda fd: isatty_value)
+ monkeypatch.setattr("sys.stdout.isatty", lambda: isatty_value)
+ monkeypatch.setattr("sys.stdout.fileno", lambda: 1)
+
+ # Act / Assert
+ assert_cprint(capsys, expected, "text", color="cyan", **kwargs)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn'
'--exclude=.svnignore' old/termcolor-2.5.0/tox.ini new/termcolor-3.2.0/tox.ini
--- old/termcolor-2.5.0/tox.ini 2024-10-06 21:46:09.000000000 +0200
+++ new/termcolor-3.2.0/tox.ini 2025-10-25 18:17:06.000000000 +0200
@@ -3,7 +3,8 @@
tox>=4.2
env_list =
lint
- py{py3, 313, 312, 311, 310, 39}
+ mutmut
+ py{py3, 315, 314, 313, 312, 311, 310, 315t, 314t, 313t}
[testenv]
extras =
@@ -14,6 +15,7 @@
{envpython} -m pytest \
--cov termcolor \
--cov tests \
+ --cov-context test \
--cov-report html \
--cov-report term \
--cov-report xml \
@@ -22,8 +24,16 @@
[testenv:lint]
skip_install = true
deps =
- pre-commit
+ pre-commit-uv
pass_env =
PRE_COMMIT_COLOR
commands =
pre-commit run --all-files --show-diff-on-failure
+
+[testenv:mutmut]
+deps =
+ mutmut
+extras =
+ tests
+commands =
+ mutmut {posargs:run}