From 65342ea458ed2b17be570114a1a63d132c687df0 Mon Sep 17 00:00:00 2001 From: Jon Dufresne Date: Tue, 5 Jul 2022 18:32:37 -0700 Subject: [PATCH 1/2] Add type annotations to the project and run mypy on CI This helps library consumers ensure that they are using the provided API correctly. The project includes a py.typed file for PEP-561 compliance. This also helps ensure internal correctness and consistency. mypy also runs on the tests, to demonstrate the test cases are representative and real use cases. Type checking will run on all pull requests through GitHub actions and pre-commit. --- .pre-commit-config.yaml | 7 +++++++ setup.cfg | 1 + src/termcolor/py.typed | 0 src/termcolor/termcolor.py | 16 ++++++++++++++-- tests/test_termcolor.py | 35 +++++++++++++++++++++++------------ 5 files changed, 45 insertions(+), 14 deletions(-) create mode 100644 src/termcolor/py.typed diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ef9e953..7795f9b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -58,5 +58,12 @@ repos: - id: prettier args: [--prose-wrap=always, --print-width=88] + - repo: https://github.com/pre-commit/mirrors-mypy + rev: v0.961 + hooks: + - id: mypy + args: [--strict, --pretty, --show-error-codes] + additional_dependencies: [pytest, types-setuptools] + ci: autoupdate_schedule: quarterly diff --git a/setup.cfg b/setup.cfg index 83023c3..3a6acc9 100644 --- a/setup.cfg +++ b/setup.cfg @@ -40,6 +40,7 @@ project_urls = [options] packages = find: python_requires = >=3.7 +include_package_data = true package_dir = =src setup_requires = setuptools-scm diff --git a/src/termcolor/py.typed b/src/termcolor/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/src/termcolor/termcolor.py b/src/termcolor/termcolor.py index 6b7af82..1eed6d5 100644 --- a/src/termcolor/termcolor.py +++ b/src/termcolor/termcolor.py @@ -23,6 +23,7 @@ """ANSI color formatting for output in terminal.""" import os +from typing import Any, Iterable, Optional __ALL__ = ["colored", "cprint"] @@ -89,7 +90,12 @@ RESET = "\033[0m" -def colored(text, color=None, on_color=None, attrs=None): +def colored( + text: str, + color: Optional[str] = None, + on_color: Optional[str] = None, + attrs: Optional[Iterable[str]] = None, +) -> str: """Colorize text. Available text colors: @@ -122,7 +128,13 @@ def colored(text, color=None, on_color=None, attrs=None): return text + RESET -def cprint(text, color=None, on_color=None, attrs=None, **kwargs): +def cprint( + text: str, + color: Optional[str] = None, + on_color: Optional[str] = None, + attrs: Optional[Iterable[str]] = None, + **kwargs: Any, +) -> None: """Print colorize text. It accepts arguments of print function. diff --git a/tests/test_termcolor.py b/tests/test_termcolor.py index f3b0c02..7bcd4c2 100644 --- a/tests/test_termcolor.py +++ b/tests/test_termcolor.py @@ -1,15 +1,16 @@ import os +from typing import Any, List, Optional import pytest from termcolor import ATTRIBUTES, COLORS, HIGHLIGHTS, colored, cprint -ALL_COLORS = list(COLORS) + [None] -ALL_HIGHLIGHTS = list(HIGHLIGHTS) + [None] -ALL_ATTRIBUTES = list(ATTRIBUTES) + [None] +ALL_COLORS = [*COLORS, None] +ALL_HIGHLIGHTS = [*HIGHLIGHTS, None] +ALL_ATTRIBUTES = [*ATTRIBUTES, None] -def setup_module(): +def setup_module() -> None: # By default, make sure no env vars already set for tests try: del os.environ["ANSI_COLORS_DISABLED"] @@ -17,11 +18,11 @@ def setup_module(): pass -def test_basic(): +def test_basic() -> None: assert colored("text") == "text\x1b[0m" -def test_sanity(): +def test_sanity() -> None: for color in ALL_COLORS: for highlight in ALL_HIGHLIGHTS: for attribute in ALL_ATTRIBUTES: @@ -31,8 +32,14 @@ def test_sanity(): def assert_cprint( - capsys, expected, text, color=None, on_color=None, attrs=None, **kwargs -): + capsys: pytest.CaptureFixture[str], + expected: str, + text: str, + color: Optional[str] = None, + on_color: Optional[str] = None, + attrs: Optional[List[str]] = None, + **kwargs: Any, +) -> None: cprint(text, color, on_color, attrs, **kwargs) captured = capsys.readouterr() print(captured.out) @@ -52,7 +59,7 @@ def assert_cprint( ("white", "\x1b[37mtext\x1b[0m"), ], ) -def test_color(capsys, color, expected): +def test_color(capsys: pytest.CaptureFixture[str], color: str, expected: str) -> None: assert colored("text", color=color) == expected assert_cprint(capsys, expected, "text", color=color) @@ -70,7 +77,9 @@ def test_color(capsys, color, expected): ("on_white", "\x1b[47mtext\x1b[0m"), ], ) -def test_on_color(capsys, on_color, expected): +def test_on_color( + capsys: pytest.CaptureFixture[str], on_color: str, expected: str +) -> None: assert colored("text", on_color=on_color) == expected assert_cprint(capsys, expected, "text", on_color=on_color) @@ -86,7 +95,7 @@ def test_on_color(capsys, on_color, expected): ("concealed", "\x1b[8mtext\x1b[0m"), ], ) -def test_attrs(capsys, attr, expected): +def test_attrs(capsys: pytest.CaptureFixture[str], attr: str, expected: str) -> None: assert colored("text", attrs=[attr]) == expected assert_cprint(capsys, expected, "text", attrs=[attr]) @@ -108,7 +117,9 @@ def test_attrs(capsys, attr, expected): "", ], ) -def test_env_var(monkeypatch, test_env_var, test_value): +def test_env_var( + monkeypatch: pytest.MonkeyPatch, test_env_var: str, test_value: str +) -> None: """Assert nothing applied when this env var set, regardless of value.""" monkeypatch.setenv(test_env_var, test_value) assert colored("text", color="red") == "text" From 50eb6f42f874868acc3e73fc440534c730ca55c7 Mon Sep 17 00:00:00 2001 From: Jon Dufresne Date: Wed, 6 Jul 2022 15:29:36 -0700 Subject: [PATCH 2/2] Use from __future__ import annotations --- setup.py | 2 ++ src/termcolor/__init__.py | 2 ++ src/termcolor/termcolor.py | 16 +++++++++------- tests/test_termcolor.py | 10 ++++++---- 4 files changed, 19 insertions(+), 11 deletions(-) diff --git a/setup.py b/setup.py index e5127ec..e51225e 100755 --- a/setup.py +++ b/setup.py @@ -21,6 +21,8 @@ # # Author: Konstantin Lepa +from __future__ import annotations + from setuptools import setup diff --git a/src/termcolor/__init__.py b/src/termcolor/__init__.py index 75df09d..23473ba 100644 --- a/src/termcolor/__init__.py +++ b/src/termcolor/__init__.py @@ -1,3 +1,5 @@ +from __future__ import annotations + from termcolor.termcolor import ( ATTRIBUTES, COLORS, diff --git a/src/termcolor/termcolor.py b/src/termcolor/termcolor.py index 1eed6d5..b4b9fe6 100644 --- a/src/termcolor/termcolor.py +++ b/src/termcolor/termcolor.py @@ -22,8 +22,10 @@ """ANSI color formatting for output in terminal.""" +from __future__ import annotations + import os -from typing import Any, Iterable, Optional +from typing import Any, Iterable __ALL__ = ["colored", "cprint"] @@ -92,9 +94,9 @@ def colored( text: str, - color: Optional[str] = None, - on_color: Optional[str] = None, - attrs: Optional[Iterable[str]] = None, + color: str | None = None, + on_color: str | None = None, + attrs: Iterable[str] | None = None, ) -> str: """Colorize text. @@ -130,9 +132,9 @@ def colored( def cprint( text: str, - color: Optional[str] = None, - on_color: Optional[str] = None, - attrs: Optional[Iterable[str]] = None, + color: str | None = None, + on_color: str | None = None, + attrs: Iterable[str] | None = None, **kwargs: Any, ) -> None: """Print colorize text. diff --git a/tests/test_termcolor.py b/tests/test_termcolor.py index 7bcd4c2..172c9de 100644 --- a/tests/test_termcolor.py +++ b/tests/test_termcolor.py @@ -1,5 +1,7 @@ +from __future__ import annotations + import os -from typing import Any, List, Optional +from typing import Any import pytest @@ -35,9 +37,9 @@ def assert_cprint( capsys: pytest.CaptureFixture[str], expected: str, text: str, - color: Optional[str] = None, - on_color: Optional[str] = None, - attrs: Optional[List[str]] = None, + color: str | None = None, + on_color: str | None = None, + attrs: list[str] | None = None, **kwargs: Any, ) -> None: cprint(text, color, on_color, attrs, **kwargs)