jsbueno/terminedia

`redirected stdin is pseudofile, has no fileno()`

paw-lu opened this issue · 3 comments

When trying to test this libary via pytest, an exception is raised: redirected stdin is pseudofile, has no fileno(). This is due to this line:

self.fd = sys.stdin.fileno()

Minimal reproducible

"""script.py"""
import io

import terminedia
from PIL import Image


def foo():
    pil_image = Image.open("image_file.jpeg")

    size = terminedia.V2(x=60, y=30)

    shape = terminedia.shape(
        pil_image,
        size=size,
        promote=True,
        resolution="square",
    )

    output = io.StringIO()
    shape.render(output=output, backend="ANSI")
"""Test script.py"""
import script

def test_foo():
    assert script.foo() is None
$ pytest
platform darwin -- Python 3.9.5, pytest-6.2.4, py-1.10.0, pluggy-0.13.1
rootdir: /Users/pawlu/Documents/scratch/pedia
collected 1 item

test_debug.py F                                                                                                    [100%]

======================================================== FAILURES ========================================================
_______________________________________________________ test_doit ________________________________________________________

    def test_doit():
>       assert draw_debug.doti() is None

test_debug.py:4:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
draw_debug.py:76: in doti
    shape.render(output=output, backend="ANSI")
.venv/lib/python3.9/site-packages/terminedia/image.py:645: in render
    self._render_using_screen(output, backend)
.venv/lib/python3.9/site-packages/terminedia/image.py:687: in _render_using_screen
    sc.commands.replay(output)
.venv/lib/python3.9/site-packages/terminedia/backend_common.py:217: in replay
    func(arg, file=file)
.venv/lib/python3.9/site-packages/terminedia/backend_common.py:256: in set_fg_color
    super().set_fg_color(color, file=file)
.venv/lib/python3.9/site-packages/terminedia/terminal.py:474: in set_fg_color
    self.SGR(38, 2, *Color(color), file=file)
.venv/lib/python3.9/site-packages/terminedia/terminal.py:304: in SGR
    self.CSI(*args, "m", file=file)
.venv/lib/python3.9/site-packages/terminedia/terminal.py:293: in CSI
    self._print("\x1b[", args, command, file=file)
.venv/lib/python3.9/site-packages/terminedia/terminal.py:157: in _print
    with UnblockTTY():
.venv/lib/python3.9/site-packages/terminedia/terminal.py:92: in __enter__
    self.fd = sys.stdin.fileno()
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <_pytest.capture.DontReadFromInput object at 0x10b29e670>

    def fileno(self) -> int:
>       raise UnsupportedOperation("redirected stdin is pseudofile, has no fileno()")
E       io.UnsupportedOperation: redirected stdin is pseudofile, has no fileno()

.venv/lib/python3.9/site-packages/_pytest/capture.py:229: UnsupportedOperation
================================================ short test summary info =================================================
FAILED test_debug.py::test_doit - io.UnsupportedOperation: redirected stdin is pseudofile, has no fileno()
=================================================== 1 failed in 0.51s ====================================================

Thank you for the feedback.

Please, for teh time being use the -s switch in pytest, which avoids this redirection.

If you notice pytest output, it also extends it with the --DISPLAY and --DELAY options: some of the tests do make use of the library itself for a visual on screen output which is displayed with these options.

Please, for teh time being use the -s switch in pytest, which avoids this redirection.

Got it!

In my case I wanted to keep Pytest's capture functionality, and not diable it globally, so I ended up using this pytest fixture to temporarity disable pytest's capturing.

Leaving it here in case it's useful for others.

import pytest
from _pytest.config import _PluggyPlugin
from _pytest.config import Config


@pytest.fixture
def disable_capture(pytestconfig: Config) -> ContextManager[_PluggyPlugin]:
    """Disable pytest's capture."""
    # https://github.com/pytest-dev/pytest/issues/
    # 1599?utm_source=pocket_mylist#issuecomment-556327594
    @contextlib.contextmanager
    def _disable_capture() -> Iterator[_PluggyPlugin]:
        """Disable pytest's capture."""
        capmanager = pytestconfig.pluginmanager.getplugin("capturemanager")
        try:
            capmanager.suspend_global_capture(in_=True)
            yield capmanager
        finally:
            capmanager.resume_global_capture()

    return _disable_capture()