diff --git a/.circleci/config.yml b/.circleci/config.yml index 839c551..1d91394 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -27,6 +27,9 @@ jobs: - codecov/upload: file: coverage.xml - run: pipenv run ./build.sh + - run: pip install naughtty + - run: pip install dist/* + - run: ./test-cli.sh - run: if [[ -z "${CIRCLE_TAG}" ]]; then circleci-agent step halt; fi - run: pipenv run twine upload dist/* diff --git a/.vscode/settings.json b/.vscode/settings.json index d3d65ba..113921f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,6 +5,7 @@ "CMYB", "dinject", "epilog", + "fstat", "naughtty", "RGBA", "Strikethrough" diff --git a/Pipfile.lock b/Pipfile.lock index f0ee325..d8aed7b 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -19,10 +19,10 @@ "develop": { "ansiscape": { "hashes": [ - "sha256:dc2ba6e6c38899ede7d2171dcef3c1b6f70926ecba12b55b305bcbd230c077b1" + "sha256:df42d4f1803407082653c13871769af1772025fcf27d0c2d774af6cfe982b51d" ], "markers": "python_version >= '3.8'", - "version": "==1.0.0" + "version": "==1.0.2" }, "attrs": { "hashes": [ @@ -115,11 +115,11 @@ }, "click": { "hashes": [ - "sha256:3fab8aeb8f15f5452ae7511ad448977b3417325bceddd53df87e0bb81f3a8cf8", - "sha256:7027bc7bbafaab8b2c2816861d8eb372429ee3c02e193fc2f93d6c4ab9de49c5" + "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3", + "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b" ], "markers": "python_version >= '3.6'", - "version": "==8.0.2" + "version": "==8.0.3" }, "colorama": { "hashes": [ @@ -214,11 +214,11 @@ }, "flake8": { "hashes": [ - "sha256:07528381786f2a6237b061f6e96610a4167b226cb926e2aa2b6b1d78057c576b", - "sha256:bf8fd333346d844f616e8d47905ef3a3384edae6b4e9beb0c5101e25e3110907" + "sha256:479b1304f72536a55948cb40a32dce8bb0ffe3501e26eaf292c7e60eb5e0428d", + "sha256:806e034dda44114815e23c16ef92f95c91e4c71100ff52813adf7132a6ad870d" ], "index": "pypi", - "version": "==3.9.2" + "version": "==4.0.1" }, "ghp-import": { "hashes": [ @@ -411,10 +411,10 @@ }, "naughtty": { "hashes": [ - "sha256:3887393743831d2e1dc6082e5db0d110c4db6eb226a7ea16411cf6cfa49677df" + "sha256:127235a0ecd8f2e5db97a271732d9ce41299f7f6d59b258e1c63d306cdb1ae35" ], "markers": "python_version >= '3.8'", - "version": "==1.0.0" + "version": "==1.0.1" }, "packaging": { "hashes": [ @@ -464,11 +464,11 @@ }, "pycodestyle": { "hashes": [ - "sha256:514f76d918fcc0b55c6680472f0a37970994e07bbb80725808c17089be302068", - "sha256:c389c1d06bf7904078ca03399a4816f974a1d590090fecea0c63ec26ebaf1cef" + "sha256:720f8b39dde8b293825e7ff02c475f3077124006db4f440dcbc9a20b76548a20", + "sha256:eddd5847ef438ea1c7870ca7eb78a9d47ce0cdb4851a5523949f2601d0cbbe7f" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.7.0" + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "version": "==2.8.0" }, "pycparser": { "hashes": [ @@ -480,11 +480,11 @@ }, "pyflakes": { "hashes": [ - "sha256:7893783d01b8a89811dd72d7dfd4d84ff098e5eed95cfa8905b22bbffe52efc3", - "sha256:f5bc8ecabc05bb9d291eb5203d6810b49040f6ff446a756326104746cc00c1db" + "sha256:05a85c2872edf37a4ed30b0cce2f6093e1d0581f8c19d7393122da7e25b2b24c", + "sha256:3bb3a3f256f4b7968c9c788781e4ff07dce46bdf12339dcda61053375426ee2e" ], "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.3.1" + "version": "==2.4.0" }, "pygments": { "hashes": [ @@ -723,7 +723,7 @@ "sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece", "sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844" ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4.0'", "version": "==1.26.7" }, "watchdog": { diff --git a/README.md b/README.md index 8416149..05aa683 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ ls --color | ansiscape ```text -[{"background": -1, "foreground": 4, "weight": 1},"ansiscape",{"background": -1},"\n",{"foreground": 4, "weight": 1},"ansiscape.egg-info",{"background": -1},"\n",{"foreground": 2, "weight": 1},"build.sh",{"background": -1},"\ncoverage.xml\n",{"foreground": 4, "weight": 1},"dist",{"background": -1},"\n",{"foreground": 4, "weight": 1},"docs",{"background": -1},"\n",{"foreground": 4, "weight": 1},"htmlcov",{"background": -1},"\nLICENSE\n",{"foreground": 2, "weight": 1},"lint.sh",{"background": -1},"\nMANIFEST.in\nmkdocs.yml\nmypy.ini\nPipfile\nPipfile.lock\npyproject.toml\nREADME.md\nsetup.py\n",{"foreground": 4, "weight": 1},"tests",{"background": -1},"\n",{"foreground": 2, "weight": 1},"test.sh",{"background": -1}] +[{"background": -1, "foreground": 4, "weight": 1},"ansiscape",{"background": -1},"\n",{"foreground": 4, "weight": 1},"ansiscape.egg-info",{"background": -1},"\n",{"foreground": 2, "weight": 1},"build.sh",{"background": -1},"\ncoverage.xml\n",{"foreground": 4, "weight": 1},"dist",{"background": -1},"\n",{"foreground": 4, "weight": 1},"docs",{"background": -1},"\n",{"foreground": 4, "weight": 1},"htmlcov",{"background": -1},"\nLICENSE\n",{"foreground": 2, "weight": 1},"lint.sh",{"background": -1},"\nMANIFEST.in\nmkdocs.yml\nmypy.ini\nPipfile\nPipfile.lock\npyproject.toml\nREADME.md\nsetup.py\n",{"foreground": 2, "weight": 1},"test-cli.sh",{"background": -1},"\n",{"foreground": 4, "weight": 1},"tests",{"background": -1},"\n",{"foreground": 2, "weight": 1},"test.sh",{"background": -1}] ``` diff --git a/ansiscape/__main__.py b/ansiscape/__main__.py index d1f1bd4..b6461cb 100644 --- a/ansiscape/__main__.py +++ b/ansiscape/__main__.py @@ -3,6 +3,7 @@ from typing import List, TextIO from ansiscape import Sequence, get_version +from ansiscape.checks import should_emit_codes from ansiscape.example import make_example @@ -25,11 +26,24 @@ def cli_entry( epilog="Made with love by Cariad Eccleston: https://github.com/cariad/ansiscape", ) + parser.add_argument( + "--check", + action="store_true", + help="checks if codes should be emitted", + ) + parser.add_argument("--example", action="store_true", help="print an example") parser.add_argument("--version", action="store_true", help="print the version") args = parser.parse_args(cli_args[1:]) + if args.check: + if should_emit_codes(): + out_pipe.write("yes\n") + else: + out_pipe.write("no\n") + return + if args.example: out_pipe.write(str(make_example())) return diff --git a/ansiscape/checks.py b/ansiscape/checks.py new file mode 100644 index 0000000..1bd0b9b --- /dev/null +++ b/ansiscape/checks.py @@ -0,0 +1,7 @@ +from os import fstat + + +def should_emit_codes() -> bool: + """Returns `True` if ANSI escape codes should be emitted.""" + + return fstat(0) == fstat(1) diff --git a/docs/checks.md b/docs/checks.md new file mode 100644 index 0000000..df0449d --- /dev/null +++ b/docs/checks.md @@ -0,0 +1,5 @@ +# Checks + +`ansiscape.checks` contains functions to check the state of your application. + +`should_emit_codes()` returns `True` if ANSI escape codes should be emitted. It'll return `False`, for example, if the script appears to be running non-interactively. diff --git a/docs/cli.md b/docs/cli.md index 71cc59f..d2da8f6 100644 --- a/docs/cli.md +++ b/docs/cli.md @@ -44,7 +44,7 @@ ls --color | ansiscape ```text -[{"background": -1, "foreground": 4, "weight": 1},"ansiscape",{"background": -1},"\n",{"foreground": 4, "weight": 1},"ansiscape.egg-info",{"background": -1},"\n",{"foreground": 2, "weight": 1},"build.sh",{"background": -1},"\ncoverage.xml\n",{"foreground": 4, "weight": 1},"dist",{"background": -1},"\n",{"foreground": 4, "weight": 1},"docs",{"background": -1},"\n",{"foreground": 4, "weight": 1},"htmlcov",{"background": -1},"\nLICENSE\n",{"foreground": 2, "weight": 1},"lint.sh",{"background": -1},"\nMANIFEST.in\nmkdocs.yml\nmypy.ini\nPipfile\nPipfile.lock\npyproject.toml\nREADME.md\nsetup.py\n",{"foreground": 4, "weight": 1},"tests",{"background": -1},"\n",{"foreground": 2, "weight": 1},"test.sh",{"background": -1}] +[{"background": -1, "foreground": 4, "weight": 1},"ansiscape",{"background": -1},"\n",{"foreground": 4, "weight": 1},"ansiscape.egg-info",{"background": -1},"\n",{"foreground": 2, "weight": 1},"build.sh",{"background": -1},"\ncoverage.xml\n",{"foreground": 4, "weight": 1},"dist",{"background": -1},"\n",{"foreground": 4, "weight": 1},"docs",{"background": -1},"\n",{"foreground": 4, "weight": 1},"htmlcov",{"background": -1},"\nLICENSE\n",{"foreground": 2, "weight": 1},"lint.sh",{"background": -1},"\nMANIFEST.in\nmkdocs.yml\nmypy.ini\nPipfile\nPipfile.lock\npyproject.toml\nREADME.md\nsetup.py\n",{"foreground": 2, "weight": 1},"test-cli.sh",{"background": -1},"\n",{"foreground": 4, "weight": 1},"tests",{"background": -1},"\n",{"foreground": 2, "weight": 1},"test.sh",{"background": -1}] ``` diff --git a/mkdocs.yml b/mkdocs.yml index 31befde..7488d49 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -3,6 +3,7 @@ markdown_extensions: nav: - index.md - cli.md + - checks.md - make.md - read.md - interpretation.md diff --git a/test-cli.sh b/test-cli.sh new file mode 100755 index 0000000..569c70a --- /dev/null +++ b/test-cli.sh @@ -0,0 +1,16 @@ +#!/bin/env bash + +set -euo pipefail + +assert() { + if [[ "${1}" == "${2}" ]]; then + return + fi + + echo "Expected \"${2}\" but got \"${1}\" 🔥" + exit 1 +} + +assert "$(ansiscape --version)" "${CIRCLE_TAG:-"-1.-1.-1"}" +assert "$(ansiscape --check)" "no" +assert "$(naughtty ansiscape --check)" $'yes\r' diff --git a/tests/test_checks.py b/tests/test_checks.py new file mode 100644 index 0000000..80c22c8 --- /dev/null +++ b/tests/test_checks.py @@ -0,0 +1,5 @@ +from ansiscape.checks import should_emit_codes + + +def test_should_emit_codes() -> None: + assert not should_emit_codes() diff --git a/tests/test_main.py b/tests/test_main.py index 24dd06a..b27ad0a 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -3,6 +3,12 @@ from ansiscape.__main__ import cli_entry +def test_cli_entry__check() -> None: + stdout = StringIO() + cli_entry(cli_args=["ansiscape", "--check"], out_pipe=stdout) + assert stdout.getvalue() == "no\n" + + def test_cli_entry__example() -> None: stdout = StringIO() cli_entry(cli_args=["ansiscape", "--example"], out_pipe=stdout)