From fc6bac8088b5548bae20da4e7251953f0739e17a Mon Sep 17 00:00:00 2001 From: Jeff Shipley Date: Sat, 10 Feb 2024 22:05:04 -0700 Subject: [PATCH 1/6] stdin works on Windows, too, without regressing issue #5 --- rexi/cli.py | 36 ++++++++++++++++-------------------- tests/test_cli.py | 23 ++++++++++++++--------- 2 files changed, 30 insertions(+), 29 deletions(-) diff --git a/rexi/cli.py b/rexi/cli.py index fcbe354..ca85f15 100644 --- a/rexi/cli.py +++ b/rexi/cli.py @@ -1,5 +1,4 @@ import os -import select import sys from typing import Annotated, Optional @@ -9,6 +8,13 @@ app = typer.Typer() +def is_stdin_a_tty() -> bool: + """Wrapper for sys.stdin.isatty. + + Trying to directly mock/patch sys.stdin.isatty wasn't working, + but it's easy to patch a function that calls sys.stdin.isatty() + """ + return sys.stdin.isatty() # noinspection SpellCheckingInspection @app.command("rexi") @@ -40,30 +46,20 @@ def rexi_cli( ) -> None: if input_file: input_text = input_file.read() - else: - """ - Yep this part is abit ugly. - couldn't find a better way to implement it so it will work with `typer`, `pytest` and `textual` - """ # noqa: E501 - if not select.select( - [ - sys.stdin, - ], - [], - [], - 0.0, - )[0]: - raise typer.BadParameter( - "stdin is empty, " - "please provide text thru the stdin " - "or use the `-i` flag" - ) + elif not is_stdin_a_tty(): input_text = sys.stdin.read() try: os.close(sys.stdin.fileno()) except OSError: pass - sys.stdin = open("/dev/tty", "rb") # type: ignore[assignment] + # Windows uses "con:" for stdin device name + sys.stdin = open("con:" if os.name == "nt" else "/dev/tty", "rb") # type: ignore[assignment] + else: + raise typer.BadParameter( + "stdin is empty, " + "please provide text thru the stdin " + "or use the `-i` flag" + ) app: RexiApp[int] = RexiApp( input_text, initial_mode=initial_mode, initial_pattern=initial_pattern ) diff --git a/tests/test_cli.py b/tests/test_cli.py index c4fcc0c..20c2976 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1,3 +1,4 @@ +import os from io import BytesIO from pathlib import Path from unittest.mock import Mock @@ -8,7 +9,7 @@ from rexi.cli import app -def test_on_args(monkeypatch: MonkeyPatch) -> None: +def test_no_args(monkeypatch: MonkeyPatch) -> None: """ Couldn't find a better way to test the CLI without patching everything :( """ @@ -18,17 +19,20 @@ def test_on_args(monkeypatch: MonkeyPatch) -> None: class_mock = Mock() instance_mock = Mock() open_mock = Mock() - select = Mock() + read_mock = Mock() + isatty_mock = Mock() with monkeypatch.context(): - select.return_value = [[True]] + read_mock.return_value = "" class_mock.return_value = instance_mock - monkeypatch.setattr("select.select", select) + isatty_mock.return_value = False + monkeypatch.setattr("rexi.cli.is_stdin_a_tty", isatty_mock) monkeypatch.setattr("rexi.cli.RexiApp", class_mock) monkeypatch.setattr("builtins.open", open_mock) runner.invoke(app, input=a) - - open_mock.assert_called_once_with("/dev/tty", "rb") + open_mock.assert_called_once_with( + "con:" if os.name == "nt" else "/dev/tty", "rb" + ) class_mock.assert_called_once_with( text.decode(), initial_mode=None, initial_pattern=None ) @@ -79,9 +83,10 @@ def test_no_stdin_error(monkeypatch: MonkeyPatch) -> None: Couldn't find a better way to test the CLI without patching everything :( """ runner = CliRunner() - select = Mock() + class_mock = Mock() + isatty_mock = Mock() with monkeypatch.context(): - select.return_value = [[]] - monkeypatch.setattr("select.select", select) + isatty_mock.return_value = True + monkeypatch.setattr("rexi.cli.is_stdin_a_tty", isatty_mock) result = runner.invoke(app) assert "Invalid value" in result.output From bfce6865f5085a7b64a1e4386250092fedf81f29 Mon Sep 17 00:00:00 2001 From: Jeff Shipley Date: Sun, 11 Feb 2024 01:10:12 -0700 Subject: [PATCH 2/6] fix lint --- tests/test_cli.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_cli.py b/tests/test_cli.py index 20c2976..e4878bf 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -83,7 +83,6 @@ def test_no_stdin_error(monkeypatch: MonkeyPatch) -> None: Couldn't find a better way to test the CLI without patching everything :( """ runner = CliRunner() - class_mock = Mock() isatty_mock = Mock() with monkeypatch.context(): isatty_mock.return_value = True From d2726505c1672afbd329c6c055bac5f15c0e27f2 Mon Sep 17 00:00:00 2001 From: Jeff Shipley Date: Tue, 13 Feb 2024 20:51:18 -0700 Subject: [PATCH 3/6] Added windows-latest to github actions matrix --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 64c1de9..2f4c632 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -36,7 +36,7 @@ jobs: fail-fast: false matrix: python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] - os: [ubuntu-latest] + os: [ubuntu-latest, windows-latest] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 From ba964835f413fef7cc84314ce5db8206b0af5c1c Mon Sep 17 00:00:00 2001 From: Jeff Shipley Date: Wed, 14 Feb 2024 20:17:28 -0700 Subject: [PATCH 4/6] Added isatty test to get full coverage. Also removed '-rf' from rm command to fix windows matrix tests --- .github/workflows/tests.yml | 2 +- tests/test_cli.py | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2f4c632..a746558 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -51,7 +51,7 @@ jobs: key: venv-${{ runner.os }}-${{ matrix.python-version }}-${{ hashFiles('**/pyproject.toml') }} - name: install deps if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' - run: rm -rf poetry.lock && make install + run: rm poetry.lock && make install - name: test run: make test - uses: codecov/codecov-action@v3 diff --git a/tests/test_cli.py b/tests/test_cli.py index e4878bf..8729254 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -3,10 +3,11 @@ from pathlib import Path from unittest.mock import Mock +import pytest from _pytest.monkeypatch import MonkeyPatch from typer.testing import CliRunner -from rexi.cli import app +from rexi.cli import app, is_stdin_a_tty def test_no_args(monkeypatch: MonkeyPatch) -> None: @@ -89,3 +90,12 @@ def test_no_stdin_error(monkeypatch: MonkeyPatch) -> None: monkeypatch.setattr("rexi.cli.is_stdin_a_tty", isatty_mock) result = runner.invoke(app) assert "Invalid value" in result.output + + +@pytest.mark.parametrize('input_value', [True, False]) +def test_is_stdin_a_tty(monkeypatch: MonkeyPatch, input_value: bool) -> None: + isatty_mock = Mock() + with monkeypatch.context(): + isatty_mock.return_value = input_value + monkeypatch.setattr("rexi.cli.sys.stdin.isatty", isatty_mock) + assert is_stdin_a_tty() == input_value \ No newline at end of file From b3ada696480361851a7cb40ff9c5f9def40a6d6b Mon Sep 17 00:00:00 2001 From: Jeff Shipley Date: Thu, 15 Feb 2024 19:00:09 -0700 Subject: [PATCH 5/6] Install make on windows before using it. --- .github/workflows/tests.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index a746558..2fe7b28 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -49,6 +49,9 @@ jobs: with: path: .venv key: venv-${{ runner.os }}-${{ matrix.python-version }}-${{ hashFiles('**/pyproject.toml') }} + - name: install windows deps + if: matrix.os == 'windows-latest' + run: choco install make - name: install deps if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' run: rm poetry.lock && make install From 441b086b0683c02a009332b8475c3f3d744bb6f9 Mon Sep 17 00:00:00 2001 From: roy reznik Date: Fri, 16 Feb 2024 13:27:36 +0200 Subject: [PATCH 6/6] Update tests.yml --- .github/workflows/tests.yml | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 2fe7b28..172e1c9 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -36,7 +36,7 @@ jobs: fail-fast: false matrix: python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] - os: [ubuntu-latest, windows-latest] + os: [ubuntu-latest] runs-on: ${{ matrix.os }} steps: - uses: actions/checkout@v4 @@ -49,14 +49,12 @@ jobs: with: path: .venv key: venv-${{ runner.os }}-${{ matrix.python-version }}-${{ hashFiles('**/pyproject.toml') }} - - name: install windows deps - if: matrix.os == 'windows-latest' - run: choco install make - name: install deps if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true' - run: rm poetry.lock && make install + run: rm -rf poetry.lock && make install - name: test run: make test - uses: codecov/codecov-action@v3 with: - token: ${{ secrets.CODECOV_TOKEN }} \ No newline at end of file + token: ${{ secrets.CODECOV_TOKEN }} +