diff --git a/packages/markitdown/src/markitdown/__main__.py b/packages/markitdown/src/markitdown/__main__.py index b2575540b6..31c0aea8e9 100644 --- a/packages/markitdown/src/markitdown/__main__.py +++ b/packages/markitdown/src/markitdown/__main__.py @@ -5,6 +5,7 @@ import os import sys import codecs +import io from typing import Any, Dict from textwrap import dedent from importlib.metadata import entry_points @@ -248,8 +249,9 @@ def main(): markitdown = MarkItDown(enable_plugins=args.use_plugins) if args.filename is None: + # Windows pipe-backed stdin can report seekable() even though it cannot rewind. result = markitdown.convert_stream( - sys.stdin.buffer, + io.BytesIO(sys.stdin.buffer.read()), stream_info=stream_info, keep_data_uris=args.keep_data_uris, ) diff --git a/packages/markitdown/tests/test_cli_misc.py b/packages/markitdown/tests/test_cli_misc.py index cf6c9ccc78..c059d74477 100644 --- a/packages/markitdown/tests/test_cli_misc.py +++ b/packages/markitdown/tests/test_cli_misc.py @@ -1,6 +1,11 @@ #!/usr/bin/env python3 -m pytest +import io import subprocess +import sys +from types import SimpleNamespace + from markitdown import __version__ +from markitdown.__main__ import main # This file contains CLI tests that are not directly tested by the FileTestVectors. # This includes things like help messages, version numbers, and invalid flags. @@ -8,7 +13,9 @@ def test_version() -> None: result = subprocess.run( - ["python", "-m", "markitdown", "--version"], capture_output=True, text=True + [sys.executable, "-m", "markitdown", "--version"], + capture_output=True, + text=True, ) assert result.returncode == 0, f"CLI exited with error: {result.stderr}" @@ -17,7 +24,9 @@ def test_version() -> None: def test_invalid_flag() -> None: result = subprocess.run( - ["python", "-m", "markitdown", "--foobar"], capture_output=True, text=True + [sys.executable, "-m", "markitdown", "--foobar"], + capture_output=True, + text=True, ) assert result.returncode != 0, f"CLI exited with error: {result.stderr}" @@ -27,6 +36,25 @@ def test_invalid_flag() -> None: assert "SYNTAX" in result.stderr, "Expected 'SYNTAX' to appear in STDERR" +def test_windows_pipe_input_is_buffered_before_conversion(monkeypatch, capsys) -> None: + class WindowsPipe(io.BytesIO): + def seek(self, offset: int, whence: int = io.SEEK_SET) -> int: + if whence == io.SEEK_END: + return super().seek(offset, whence) + return 0 + + stdin = SimpleNamespace( + buffer=WindowsPipe(b"

Test HTML

") + ) + monkeypatch.setattr(sys, "stdin", stdin) + monkeypatch.setattr(sys, "argv", ["markitdown", "-x", "html"]) + + main() + + captured = capsys.readouterr() + assert captured.out.strip() == "# Test HTML" + + if __name__ == "__main__": """Runs this file's tests from the command line.""" test_version() diff --git a/packages/markitdown/tests/test_cli_vectors.py b/packages/markitdown/tests/test_cli_vectors.py index 547c7c0001..04e0754b16 100644 --- a/packages/markitdown/tests/test_cli_vectors.py +++ b/packages/markitdown/tests/test_cli_vectors.py @@ -4,6 +4,7 @@ import pytest import subprocess import locale +import sys from typing import List if __name__ == "__main__": @@ -46,7 +47,7 @@ def test_output_to_stdout(shared_tmp_dir, test_vector) -> None: result = subprocess.run( [ - "python", + sys.executable, "-m", "markitdown", os.path.join(TEST_FILES_DIR, test_vector.filename), @@ -69,7 +70,7 @@ def test_output_to_file(shared_tmp_dir, test_vector) -> None: output_file = os.path.join(shared_tmp_dir, test_vector.filename + ".output") result = subprocess.run( [ - "python", + sys.executable, "-m", "markitdown", "-o", @@ -83,7 +84,7 @@ def test_output_to_file(shared_tmp_dir, test_vector) -> None: assert result.returncode == 0, f"CLI exited with error: {result.stderr}" assert os.path.exists(output_file), f"Output file not created: {output_file}" - with open(output_file, "r") as f: + with open(output_file, "r", encoding="utf-8") as f: output_data = f.read() for test_string in test_vector.must_include: assert test_string in output_data @@ -104,10 +105,9 @@ def test_input_from_stdin_without_hints(shared_tmp_dir, test_vector) -> None: result = subprocess.run( [ - "python", + sys.executable, "-m", "markitdown", - os.path.join(TEST_FILES_DIR, test_vector.filename), ], input=test_input, capture_output=True, @@ -135,7 +135,12 @@ def test_convert_url(shared_tmp_dir, test_vector): time.sleep(1) # Ensure we don't hit rate limits result = subprocess.run( - ["python", "-m", "markitdown", TEST_FILES_URL + "/" + test_vector.filename], + [ + sys.executable, + "-m", + "markitdown", + TEST_FILES_URL + "/" + test_vector.filename, + ], capture_output=True, text=False, ) @@ -155,7 +160,7 @@ def test_output_to_file_with_data_uris(shared_tmp_dir, test_vector) -> None: output_file = os.path.join(shared_tmp_dir, test_vector.filename + ".output") result = subprocess.run( [ - "python", + sys.executable, "-m", "markitdown", "--keep-data-uris", @@ -170,7 +175,7 @@ def test_output_to_file_with_data_uris(shared_tmp_dir, test_vector) -> None: assert result.returncode == 0, f"CLI exited with error: {result.stderr}" assert os.path.exists(output_file), f"Output file not created: {output_file}" - with open(output_file, "r") as f: + with open(output_file, "r", encoding="utf-8") as f: output_data = f.read() for test_string in test_vector.must_include: assert test_string in output_data diff --git a/packages/markitdown/tests/test_cu_converter.py b/packages/markitdown/tests/test_cu_converter.py index 20b517f764..487407bb78 100644 --- a/packages/markitdown/tests/test_cu_converter.py +++ b/packages/markitdown/tests/test_cu_converter.py @@ -932,11 +932,11 @@ def test_use_cu_reads_from_stdin(self, capsys): enable_plugins=False, cu_endpoint="https://fake-cu", ) - markitdown_instance.convert_stream.assert_called_once_with( - input_buffer, - stream_info=None, - keep_data_uris=False, - ) + + assert markitdown_instance.convert_stream.call_count == 1 + call_args, call_kwargs = markitdown_instance.convert_stream.call_args + assert call_args[0].read() == b"fake pdf" + assert call_kwargs == {"stream_info": None, "keep_data_uris": False} assert capsys.readouterr().out == "converted\n"