Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pyout.Tabular fails under pytest -s #112

Open
jwodder opened this issue Aug 6, 2020 · 2 comments
Open

pyout.Tabular fails under pytest -s #112

jwodder opened this issue Aug 6, 2020 · 2 comments

Comments

@jwodder
Copy link
Member

jwodder commented Aug 6, 2020

While attempting to test the upload functionality of https://github.com/dandi/dandi-cli, a call to pyout.Tabular() failed because curses.setupterm() "could not find terminal". This happened when invoking pytest with the -s (--capture=no) and -v options and went away when the -s option was removed (and also when I tried redirecting the output to a file). I could not create an MCVE that reproduced the problem.

For reference, the full pytest output for the failing test:

_________________________________ test_upload __________________________________

local_docker_compose = {'api_key': 'zTB6smqi0da2KlK6qt0CP6ofc3pDJHRgsyybALWJ'}
monkeypatch = <_pytest.monkeypatch.MonkeyPatch object at 0x10f75ddc0>
tmp_path = PosixPath('/private/var/folders/l7/wrkq93d133d8zpn36fmqrq0r0000gn/T/pytest-of-jwodder/pytest-32/test_upload0')

    def test_upload(local_docker_compose, monkeypatch, tmp_path):
        DIRNAME1 = "sub-anm369963"
        FILENAME1 = "sub-anm369963_ses-20170228.nwb"
        DIRNAME2 = "sub-anm372793"
        FILENAME2 = "sub-anm372793_ses-20170508.nwb"

        monkeypatch.setenv("DANDI_API_KEY", local_docker_compose["api_key"])
        dandi_instance = "local-docker-tests"

        for dirname, filename in [(DIRNAME1, FILENAME1), (DIRNAME2, FILENAME2)]:
            (tmp_path / dirname).mkdir(exist_ok=True, parents=True)
            copyfile(DANDIFILES_DIR / dirname / filename, tmp_path / dirname / filename)

        register(
            known_instances[dandi_instance],
            "Upload Test",
            "Upload Test Description",
            dandiset_path=tmp_path,
        )
        with (tmp_path / dandiset_metadata_file).open() as fp:
            metadata = yaml.safe_load(fp)
        dandi_id = metadata["identifier"]

        client = girder.get_client(known_instances[dandi_instance].girder)
        for dirname in [DIRNAME1, DIRNAME2]:
            with pytest.raises(girder.GirderNotFound):
                girder.lookup(client, collection_drafts, path=f"{dandi_id}/{dirname}")

        monkeypatch.chdir(tmp_path)
>       upload(paths=[DIRNAME1], dandi_instance=dandi_instance, devel_debug=True)

/Users/jwodder/dartmouth/dandi-cli/dandi/tests/test_upload.py:44:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
/Users/jwodder/dartmouth/dandi-cli/dandi/upload.py:506: in upload
    out = pyout.Tabular(style=pyout_style, columns=rec_fields)
/Users/jwodder/dartmouth/dandi-cli/.tox/py3/lib/python3.8/site-packages/pyout/tabular.py:155: in __init__
    streamer = TerminalStream(stream=stream, interactive=interactive)
/Users/jwodder/dartmouth/dandi-cli/.tox/py3/lib/python3.8/site-packages/pyout/tabular.py:24: in __init__
    self.term = Terminal(stream=stream,
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _

self = <blessings.Terminal object at 0x110973a00>, kind = None
stream = <_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>
force_styling = True

    def __init__(self, kind=None, stream=None, force_styling=False):
        """Initialize the terminal.

        If ``stream`` is not a tty, I will default to returning an empty
        Unicode string for all capability values, so things like piping your
        output to a file won't strew escape sequences all over the place. The
        ``ls`` command sets a precedent for this: it defaults to columnar
        output when being sent to a tty and one-item-per-line when not.

        :arg kind: A terminal string as taken by ``setupterm()``. Defaults to
            the value of the ``TERM`` environment variable.
        :arg stream: A file-like object representing the terminal. Defaults to
            the original value of stdout, like ``curses.initscr()`` does.
        :arg force_styling: Whether to force the emission of capabilities, even
            if we don't seem to be in a terminal. This comes in handy if users
            are trying to pipe your output through something like ``less -r``,
            which supports terminal codes just fine but doesn't appear itself
            to be a terminal. Just expose a command-line option, and set
            ``force_styling`` based on it. Terminal initialization sequences
            will be sent to ``stream`` if it has a file descriptor and to
            ``sys.__stdout__`` otherwise. (``setupterm()`` demands to send them
            somewhere, and stdout is probably where the output is ultimately
            headed. If not, stderr is probably bound to the same terminal.)

            If you want to force styling to not happen, pass
            ``force_styling=None``.

        """
        if stream is None:
            stream = sys.__stdout__
        try:
            stream_descriptor = (stream.fileno() if hasattr(stream, 'fileno')
                                 and callable(stream.fileno)
                                 else None)
        except IOUnsupportedOperation:
            stream_descriptor = None

        self._is_a_tty = (stream_descriptor is not None and
                         isatty(stream_descriptor))
        self._does_styling = ((self.is_a_tty or force_styling) and
                              force_styling is not None)

        # The descriptor to direct terminal initialization sequences to.
        # sys.__stdout__ seems to always have a descriptor of 1, even if output
        # is redirected.
        self._init_descriptor = (sys.__stdout__.fileno()
                                 if stream_descriptor is None
                                 else stream_descriptor)
        if self.does_styling:
            # Make things like tigetstr() work. Explicit args make setupterm()
            # work even when -s is passed to nosetests. Lean toward sending
            # init sequences to the stream if it has a file descriptor, and
            # send them to stdout as a fallback, since they have to go
            # somewhere.
>           setupterm(kind or environ.get('TERM', 'unknown'),
                      self._init_descriptor)
E           _curses.error: setupterm: could not find terminal

/Users/jwodder/dartmouth/dandi-cli/.tox/py3/lib/python3.8/site-packages/blessings/__init__.py:97: error
------------------------------ Captured log call -------------------------------
INFO     dandi:register.py:38 Registered dandiset at None/dandiset/000006/draft. Please visit and adjust metadata.
INFO     dandi:upload.py:120 Found 1 files to consider
@kyleam
Copy link
Collaborator

kyleam commented Aug 7, 2020

This happened when invoking pytest with the -s (--capture=no) and -v options and went away when the -s option was removed (and also when I tried redirecting the output to a file)

When sys.stdout.isatty() returns False (as is the case when pytest captures stdout), the force_styling parameter of blessings.Terminal is set to None, avoiding the code path in your traceback.

I could not create an MCVE that reproduced the problem.

I haven't had any luck yet either.

Are you on macOS? Was pytest called from a regular terminal (rather than, say, an IDE)?

@jwodder
Copy link
Member Author

jwodder commented Aug 7, 2020

Yes, I am on macOS 10.13.6. pytest (both the failing instance and the non-failing attempt at an MCVE) was run inside an instance of GNU screen 4.06.02 (TERM=screen-256color) inside Terminal.app 2.8.3 (TERM=xterm-256color).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy