diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 0e7709f2..f0f6d88c 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -188,6 +188,23 @@ jobs: with: version: 1.1.385 + ydiff: + name: Test compatibility with the latest version of ydiff + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: 3.12 + - name: Install dependencies + run: python .github/workflows/install_deps.py + - name: Update ydiff + run: python -m pip install -U ydiff + - name: Run tests + run: python -m pytest tests/test_ctl.py -v + docs: runs-on: ubuntu-latest steps: diff --git a/patroni/ctl.py b/patroni/ctl.py index 71c1a39d..b55cfd4d 100644 --- a/patroni/ctl.py +++ b/patroni/ctl.py @@ -1896,12 +1896,13 @@ def show_diff(before_editing: str, after_editing: str) -> None: unified_diff = difflib.unified_diff(listify(before_editing), listify(after_editing)) if sys.stdout.isatty(): - buf = io.StringIO() + buf = io.BytesIO() for line in unified_diff: - buf.write(str(line)) + buf.write(line.encode('utf-8')) buf.seek(0) class opts: + theme = 'default' side_by_side = False width = 80 tab_width = 8 diff --git a/requirements.txt b/requirements.txt index a35d1865..5b3626b1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,5 +10,5 @@ python-dateutil pysyncobj>=0.3.8 cryptography>=1.4 psutil>=2.0.0 -ydiff>=1.2.0 +ydiff>=1.2.0,<1.5,!=1.4.0,!=1.4.1 python-json-logger>=2.0.2 diff --git a/tests/test_ctl.py b/tests/test_ctl.py index 6435e775..11656d09 100644 --- a/tests/test_ctl.py +++ b/tests/test_ctl.py @@ -689,6 +689,17 @@ class TestCtl(unittest.TestCase): show_diff(b"foo:\n bar: \xc3\xb6\xc3\xb6\n".decode('utf-8'), b"foo:\n bar: \xc3\xbc\xc3\xbc\n".decode('utf-8')) + @patch('subprocess.Popen') + @patch('os.environ.get', Mock(return_value='cat')) + @patch('sys.stdout.isatty', Mock(return_value=True)) + @patch('shutil.which', Mock(return_value='cat')) + def test_show_diff_pager(self, mock_popen): + show_diff("foo:\n bar: 1\n", "foo:\n bar: 2\n") + self.assertEqual(mock_popen.return_value.stdin.write.call_count, 6) + self.assertIn(b' bar: ', mock_popen.return_value.stdin.write.call_args_list[5][0][0]) + self.assertIn(b' bar: ', mock_popen.return_value.stdin.write.call_args_list[4][0][0]) + self.assertIn(b' foo:', mock_popen.return_value.stdin.write.call_args_list[3][0][0]) + @patch('subprocess.call', return_value=1) def test_invoke_editor(self, mock_subprocess_call): os.environ.pop('EDITOR', None) diff --git a/typings/cdiff/__init__.pyi b/typings/cdiff/__init__.pyi index 4578d468..c23b6d73 100644 --- a/typings/cdiff/__init__.pyi +++ b/typings/cdiff/__init__.pyi @@ -1,5 +1,5 @@ import io from typing import Any class PatchStream: - def __init__(self, diff_hdl: io.TextIOBase) -> None: ... + def __init__(self, diff_hdl: io.BytesIOBase) -> None: ... def markup_to_pager(stream: Any, opts: Any) -> None: ... diff --git a/typings/ydiff/__init__.pyi b/typings/ydiff/__init__.pyi index 4578d468..c23b6d73 100644 --- a/typings/ydiff/__init__.pyi +++ b/typings/ydiff/__init__.pyi @@ -1,5 +1,5 @@ import io from typing import Any class PatchStream: - def __init__(self, diff_hdl: io.TextIOBase) -> None: ... + def __init__(self, diff_hdl: io.BytesIOBase) -> None: ... def markup_to_pager(stream: Any, opts: Any) -> None: ...