diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..0ab42b3 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,10 @@ +# .coveragerc to control coverage.py + +[report] +# Regexes for lines to exclude from consideration +exclude_lines = + # Have to re-enable the standard pragma: + pragma: no cover + + # Don't complain if non-runnable code isn't run: + if __name__ == .__main__.: diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..92909ef --- /dev/null +++ b/.editorconfig @@ -0,0 +1,21 @@ +# Top-most EditorConfig file +root = true + +# Unix-style newlines with a newline ending every file +[*] +end_of_line = lf +insert_final_newline = true +charset = utf-8 + +# Four-space indentation +[*.py] +indent_size = 4 +indent_style = space +trim_trailing_whitespace = true + +[tabulated_rst.py] +trim_trailing_whitespace = false + +# Two-space indentation +[*.yml] +indent_size = 2 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..fd4c7e6 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,40 @@ +name: Lint + +on: [push, pull_request] + +env: + FORCE_COLOR: 1 + +jobs: + build: + runs-on: ubuntu-20.04 + + steps: + - uses: actions/checkout@v2 + + - name: Cache + uses: actions/cache@v2 + with: + path: | + ~/.cache/pip + ~/.cache/pre-commit + key: + lint-v2-${{ hashFiles('**/setup.py') }}-${{ + hashFiles('**/.pre-commit-config.yaml') }} + restore-keys: | + lint-v2- + + - name: Set up Python + uses: actions/setup-python@v2 + with: + python-version: 3.8 + + - name: Install dependencies + run: | + python -m pip install -U pip + python -m pip install -U tox + + - name: Lint + run: tox -e lint + env: + PRE_COMMIT_COLOR: always diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..ef54839 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,69 @@ +name: Test + +on: [push, pull_request] + +env: + FORCE_COLOR: 1 + +jobs: + build: + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + python-version: ["3.6", "3.7", "3.8", "3.9-dev"] + os: [ubuntu-latest, macos-latest, windows-latest] + include: + # Include new variables for Codecov + - { codecov-flag: GHA_Ubuntu, os: ubuntu-latest } + - { codecov-flag: GHA_macOS, os: macos-latest } + - { codecov-flag: GHA_Windows, os: windows-latest } + # Deadsnakes versions + - { python-version: 3.10-dev, os: ubuntu-latest } + + steps: + - uses: actions/checkout@v2 + + - name: Set up Python ${{ matrix.python-version }} (deadsnakes) + uses: deadsnakes/action@v1.0.0 + if: contains(matrix.python-version, '3.10-dev') + with: + python-version: ${{ matrix.python-version }} + + - name: Set up Python ${{ matrix.python-version }} + if: "!contains(matrix.python-version, '3.10-dev')" + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + + - name: Get pip cache dir + id: pip-cache + run: | + echo "::set-output name=dir::$(pip cache dir)" + + - name: Cache + uses: actions/cache@v2 + with: + path: ${{ steps.pip-cache.outputs.dir }} + key: + ${{ matrix.os }}-${{ matrix.python-version }}-v2-${{ + hashFiles('**/setup.py') }} + restore-keys: | + ${{ matrix.os }}-${{ matrix.python-version }}-v2- + + - name: Install dependencies + run: | + python -m pip install -U pip + python -m pip install -U wheel + python -m pip install -U tox + + - name: Tox tests + shell: bash + run: | + tox -e py + + - name: Upload coverage + uses: codecov/codecov-action@v1 + with: + flags: ${{ matrix.codecov-flag }} + name: ${{ matrix.os }} Python ${{ matrix.python-version }} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..484d481 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,44 @@ +repos: + - repo: https://github.com/asottile/pyupgrade + rev: v2.7.2 + hooks: + - id: pyupgrade + args: ["--py36-plus"] + + - repo: https://github.com/psf/black + rev: 20.8b1 + hooks: + - id: black + args: ["--target-version", "py36"] + # override until resolved: https://github.com/psf/black/issues/402 + files: \.pyi?$ + types: [] + + - repo: https://github.com/PyCQA/isort + rev: 5.5.1 + hooks: + - id: isort + + - repo: https://gitlab.com/pycqa/flake8 + rev: 3.8.3 + hooks: + - id: flake8 + additional_dependencies: [flake8-2020, flake8-implicit-str-concat] + + - repo: https://github.com/pre-commit/pygrep-hooks + rev: v1.6.0 + hooks: + - id: python-check-blanket-noqa + + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v3.2.0 + hooks: + - id: check-merge-conflict + - id: check-toml + - id: check-yaml + + - repo: https://github.com/prettier/prettier + rev: 2.1.1 + hooks: + - id: prettier + args: [--prose-wrap=always, --print-width=88] diff --git a/README.md b/README.md index 9886bd3..34b7580 100644 --- a/README.md +++ b/README.md @@ -25,39 +25,39 @@ cprint("Attention!", "red", attrs=["bold"], file=sys.stderr) Text colors: - - grey - - red - - green - - yellow - - blue - - magenta - - cyan - - white +- grey +- red +- green +- yellow +- blue +- magenta +- cyan +- white Text highlights: - - on_grey - - on_red - - on_green - - on_yellow - - on_blue - - on_magenta - - on_cyan - - on_white +- on_grey +- on_red +- on_green +- on_yellow +- on_blue +- on_magenta +- on_cyan +- on_white Attributes: - - bold - - dark - - underline - - blink - - reverse - - concealed +- bold +- dark +- underline +- blink +- reverse +- concealed ## Terminal properties | Terminal | bold | dark | underline | blink | reverse | concealed | -|--------------|---------|------|---------- |------------|---------|-----------| +| ------------ | ------- | ---- | --------- | ---------- | ------- | --------- | | xterm | yes | no | yes | bold | yes | yes | | linux | yes | yes | bold | yes | yes | no | | rxvt | yes | no | yes | bold/black | yes | no | diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..73ff2a7 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,5 @@ +[tool.black] +target_version = ["py36"] + +[tool.isort] +profile = "black" diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..f4546ad --- /dev/null +++ b/setup.cfg @@ -0,0 +1,2 @@ +[flake8] +max_line_length = 88 diff --git a/setup.py b/setup.py index 13c6ac0..f440660 100755 --- a/setup.py +++ b/setup.py @@ -1,5 +1,4 @@ #!/usr/bin/env python -# coding: utf-8 # Copyright (c) 2008-2011 Volvox Development Team # # Permission is hereby granted, free of charge, to any person obtaining a copy @@ -33,25 +32,45 @@ def local_scheme(version): to be able to upload to Test PyPI""" return "" -setup(name='termcolor', - description='ANSII Color formatting for output in terminal.', - long_description=long_description, - long_description_content_type="text/markdown", - author='Konstantin Lepa', - license='MIT', - author_email='konstantin.lepa@gmail.com', - url='http://pypi.python.org/pypi/termcolor', - packages=find_packages(where="src"), - package_dir={"": "src"}, - use_scm_version={"local_scheme": local_scheme}, - setup_requires=["setuptools_scm"], - classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Environment :: Console', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: MIT License', - 'Operating System :: OS Independent', - 'Programming Language :: Python', - 'Topic :: Terminals' - ] - ) + +setup( + name="termcolor", + description="ANSII color formatting for output in terminal", + long_description=long_description, + long_description_content_type="text/markdown", + author="Konstantin Lepa", + url="https://github.com/hugovk/termcolor", + project_urls={"Source": "https://github.com/hugovk/termcolor"}, + license="MIT", + author_email="konstantin.lepa@gmail.com", + keywords=[ + "termcolor", + "terminal", + "ANSII color", + "ANSII colour", + "ANSII", + "color", + "colour", + "formatting", + ], + packages=find_packages(where="src"), + package_dir={"": "src"}, + use_scm_version={"local_scheme": local_scheme}, + setup_requires=["setuptools_scm"], + extras_require={"tests": ["pytest", "pytest-cov"]}, + python_requires=">=3.6", + 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", + "Programming Language :: Python :: 3.6", + "Programming Language :: Python :: 3.7", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3 :: Only", + "Topic :: Terminals", + ], +) diff --git a/src/termcolor/__init__.py b/src/termcolor/__init__.py index 5e0770d..75df09d 100644 --- a/src/termcolor/__init__.py +++ b/src/termcolor/__init__.py @@ -1,3 +1,19 @@ -from termcolor.termcolor import colored, cprint +from termcolor.termcolor import ( + ATTRIBUTES, + COLORS, + HIGHLIGHTS, + RESET, + VERSION, + colored, + cprint, +) -__all__ = ["colored", "cprint"] +__all__ = [ + "ATTRIBUTES", + "COLORS", + "HIGHLIGHTS", + "RESET", + "VERSION", + "colored", + "cprint", +] diff --git a/src/termcolor/termcolor.py b/src/termcolor/termcolor.py index f11b824..6ad6b93 100644 --- a/src/termcolor/termcolor.py +++ b/src/termcolor/termcolor.py @@ -1,4 +1,3 @@ -# coding: utf-8 # Copyright (c) 2008-2011 Volvox Development Team # # Permission is hereby granted, free of charge, to any person obtaining a copy @@ -23,64 +22,71 @@ """ANSII Color formatting for output in terminal.""" -from __future__ import print_function import os - -__ALL__ = [ 'colored', 'cprint' ] +__ALL__ = ["colored", "cprint"] VERSION = (1, 1, 0) ATTRIBUTES = dict( - list(zip([ - 'bold', - 'dark', - '', - 'underline', - 'blink', - '', - 'reverse', - 'concealed' + list( + zip( + [ + "bold", + "dark", + "", + "underline", + "blink", + "", + "reverse", + "concealed", ], - list(range(1, 9)) - )) + list(range(1, 9)), ) -del ATTRIBUTES[''] + ) +) +del ATTRIBUTES[""] HIGHLIGHTS = dict( - list(zip([ - 'on_grey', - 'on_red', - 'on_green', - 'on_yellow', - 'on_blue', - 'on_magenta', - 'on_cyan', - 'on_white' + list( + zip( + [ + "on_grey", + "on_red", + "on_green", + "on_yellow", + "on_blue", + "on_magenta", + "on_cyan", + "on_white", ], - list(range(40, 48)) - )) + list(range(40, 48)), ) + ) +) COLORS = dict( - list(zip([ - 'grey', - 'red', - 'green', - 'yellow', - 'blue', - 'magenta', - 'cyan', - 'white', + list( + zip( + [ + "grey", + "red", + "green", + "yellow", + "blue", + "magenta", + "cyan", + "white", ], - list(range(30, 38)) - )) + list(range(30, 38)), ) + ) +) -RESET = '\033[0m' +RESET = "\033[0m" def colored(text, color=None, on_color=None, attrs=None): @@ -99,8 +105,8 @@ def colored(text, color=None, on_color=None, attrs=None): colored('Hello, World!', 'red', 'on_grey', ['blue', 'blink']) colored('Hello, World!', 'green') """ - if os.getenv('ANSI_COLORS_DISABLED') is None: - fmt_str = '\033[%dm%s' + if os.getenv("ANSI_COLORS_DISABLED") is None: + fmt_str = "\033[%dm%s" if color is not None: text = fmt_str % (COLORS[color], text) @@ -124,45 +130,49 @@ def cprint(text, color=None, on_color=None, attrs=None, **kwargs): print((colored(text, color, on_color, attrs)), **kwargs) -if __name__ == '__main__': - print('Current terminal type: %s' % os.getenv('TERM')) - print('Test basic colors:') - cprint('Grey color', 'grey') - cprint('Red color', 'red') - cprint('Green color', 'green') - cprint('Yellow color', 'yellow') - cprint('Blue color', 'blue') - cprint('Magenta color', 'magenta') - cprint('Cyan color', 'cyan') - cprint('White color', 'white') - print(('-' * 78)) - - print('Test highlights:') - cprint('On grey color', on_color='on_grey') - cprint('On red color', on_color='on_red') - cprint('On green color', on_color='on_green') - cprint('On yellow color', on_color='on_yellow') - cprint('On blue color', on_color='on_blue') - cprint('On magenta color', on_color='on_magenta') - cprint('On cyan color', on_color='on_cyan') - cprint('On white color', color='grey', on_color='on_white') - print('-' * 78) - - print('Test attributes:') - cprint('Bold grey color', 'grey', attrs=['bold']) - cprint('Dark red color', 'red', attrs=['dark']) - cprint('Underline green color', 'green', attrs=['underline']) - cprint('Blink yellow color', 'yellow', attrs=['blink']) - cprint('Reversed blue color', 'blue', attrs=['reverse']) - cprint('Concealed Magenta color', 'magenta', attrs=['concealed']) - cprint('Bold underline reverse cyan color', 'cyan', - attrs=['bold', 'underline', 'reverse']) - cprint('Dark blink concealed white color', 'white', - attrs=['dark', 'blink', 'concealed']) - print(('-' * 78)) - - print('Test mixing:') - cprint('Underline red on grey color', 'red', 'on_grey', - ['underline']) - cprint('Reversed green on red color', 'green', 'on_red', ['reverse']) - +if __name__ == "__main__": + print("Current terminal type: %s" % os.getenv("TERM")) + print("Test basic colors:") + cprint("Grey color", "grey") + cprint("Red color", "red") + cprint("Green color", "green") + cprint("Yellow color", "yellow") + cprint("Blue color", "blue") + cprint("Magenta color", "magenta") + cprint("Cyan color", "cyan") + cprint("White color", "white") + print("-" * 78) + + print("Test highlights:") + cprint("On grey color", on_color="on_grey") + cprint("On red color", on_color="on_red") + cprint("On green color", on_color="on_green") + cprint("On yellow color", on_color="on_yellow") + cprint("On blue color", on_color="on_blue") + cprint("On magenta color", on_color="on_magenta") + cprint("On cyan color", on_color="on_cyan") + cprint("On white color", color="grey", on_color="on_white") + print("-" * 78) + + print("Test attributes:") + cprint("Bold grey color", "grey", attrs=["bold"]) + cprint("Dark red color", "red", attrs=["dark"]) + cprint("Underline green color", "green", attrs=["underline"]) + cprint("Blink yellow color", "yellow", attrs=["blink"]) + cprint("Reversed blue color", "blue", attrs=["reverse"]) + cprint("Concealed Magenta color", "magenta", attrs=["concealed"]) + cprint( + "Bold underline reverse cyan color", + "cyan", + attrs=["bold", "underline", "reverse"], + ) + cprint( + "Dark blink concealed white color", + "white", + attrs=["dark", "blink", "concealed"], + ) + print("-" * 78) + + print("Test mixing:") + cprint("Underline red on grey color", "red", "on_grey", ["underline"]) + cprint("Reversed green on red color", "green", "on_red", ["reverse"]) diff --git a/tests/test_termcolor.py b/tests/test_termcolor.py new file mode 100644 index 0000000..9dbc5b7 --- /dev/null +++ b/tests/test_termcolor.py @@ -0,0 +1,81 @@ +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] + + +def test_basic(): + assert colored("text") == "text\x1b[0m" + + +def test_sanity(): + for color in ALL_COLORS: + for highlight in ALL_HIGHLIGHTS: + for attribute in ALL_ATTRIBUTES: + attrs = None if attribute is None else [attribute] + colored("text", color, highlight, attrs) + cprint("text", color, highlight, attrs) + + +def assert_cprint( + capsys, expected, text, color=None, on_color=None, attrs=None, **kwargs +): + cprint(text, color, on_color, attrs, **kwargs) + captured = capsys.readouterr() + print(captured.out) + assert captured.out == expected + "\n" + + +@pytest.mark.parametrize( + "color, expected", + [ + ("grey", "\x1b[30mtext\x1b[0m"), + ("red", "\x1b[31mtext\x1b[0m"), + ("green", "\x1b[32mtext\x1b[0m"), + ("yellow", "\x1b[33mtext\x1b[0m"), + ("blue", "\x1b[34mtext\x1b[0m"), + ("magenta", "\x1b[35mtext\x1b[0m"), + ("cyan", "\x1b[36mtext\x1b[0m"), + ("white", "\x1b[37mtext\x1b[0m"), + ], +) +def test_color(capsys, color, expected): + assert colored("text", color=color) == expected + assert_cprint(capsys, expected, "text", color=color) + + +@pytest.mark.parametrize( + "on_color, expected", + [ + ("on_grey", "\x1b[40mtext\x1b[0m"), + ("on_red", "\x1b[41mtext\x1b[0m"), + ("on_green", "\x1b[42mtext\x1b[0m"), + ("on_yellow", "\x1b[43mtext\x1b[0m"), + ("on_blue", "\x1b[44mtext\x1b[0m"), + ("on_magenta", "\x1b[45mtext\x1b[0m"), + ("on_cyan", "\x1b[46mtext\x1b[0m"), + ("on_white", "\x1b[47mtext\x1b[0m"), + ], +) +def test_on_color(capsys, on_color, expected): + assert colored("text", on_color=on_color) == expected + assert_cprint(capsys, expected, "text", on_color=on_color) + + +@pytest.mark.parametrize( + "attr, expected", + [ + ("bold", "\x1b[1mtext\x1b[0m"), + ("dark", "\x1b[2mtext\x1b[0m"), + ("underline", "\x1b[4mtext\x1b[0m"), + ("blink", "\x1b[5mtext\x1b[0m"), + ("reverse", "\x1b[7mtext\x1b[0m"), + ("concealed", "\x1b[8mtext\x1b[0m"), + ], +) +def test_attrs(capsys, attr, expected): + assert colored("text", attrs=[attr]) == expected + assert_cprint(capsys, expected, "text", attrs=[attr]) diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..96e691b --- /dev/null +++ b/tox.ini @@ -0,0 +1,17 @@ +[tox] +envlist = + lint + py{36, 37, 38, 39} + +[testenv] +extras = + tests +commands = + {envpython} -m pytest --cov termcolor --cov tests --cov-report xml {posargs} +passenv = FORCE_COLOR + +[testenv:lint] +deps = pre-commit +commands = pre-commit run --all-files --show-diff-on-failure +skip_install = true +passenv = PRE_COMMIT_COLOR