Pytest: Store terminal width on config object

Created on 25 Jun 2012  路  23Comments  路  Source: pytest-dev/pytest

Originally reported by: Floris Bruynooghe (BitBucket: flub, GitHub: flub)


Some parts of py.test can not use py.io.get_terminal_width() because stdout and stderr are not attached to a real terminal at those points. Examples are the pytest_assertrepr_compare hook and xdist slaves. But since the terminal width is unlikely to change during runtime it is reasonable to store the width on the config object.

This is sufficient for pytest_assertrepr_compare, but xdist will need it's own mechanism to copy this attribute to slaves as well.

This was originally discussed at http://codespeak.net/pipermail/py-dev/2012q2/001955.html


easy help wanted enhancement

All 23 comments

Link to discussion broken.

I may dive into this one but before doing anything, is it really "safe" to think that terminal width won't change during the runtime? I can resize my graphical terminal emulator. So what would be a good implementation ? Use py.io.get_terminal_width() everywhere and fallback on a cache somewhere if not available ? Also, if stdout/stderr is not attached to a terminal, is it safe to say this terminal has a width, or should we better make the code work for width-ed terminals along with un-width-ed terminals ?

If someone has a link to the original discussion ...

I may dive into this one but before doing anything, is it really "safe" to think that terminal width won't change during the runtime?

TBH I think that's rare and I wouldn't expect it to see the new value... I would keep things simpler, just storing it and not bothering if it changes.

Also, if stdout/stderr is not attached to a terminal, is it safe to say this terminal has a width, or should we better make the code work for width-ed terminals along with un-width-ed terminals ?

Hmm that's an excellent question, I think others could chip in here.

imho we should make the assert compare repressentations independent of the terminal

(i imagine at some point streaming py.test results to a pretty browser based view that can post them to a online service/share test runs)

If the terminal width can not be determined then it all falls back to 80 This is what would happen in the streaming case so I don't think this has much influence on that use case. The change proposed here just makes the output look a little on wider terminals when using things like xdist etc.

true, this one would be a nice short term improvement

I may dive into this one but before doing anything, is it really "safe" to think that terminal width won't change during the runtime?

TBH I think that's rare and I wouldn't expect it to see the new value... I would keep things simpler, just storing it and not bothering if it changes.

It is not rare if you move windows around in a tiling window manager.
This is why I've created https://github.com/pytest-dev/py/pull/92.

So regarding https://github.com/pytest-dev/pytest/pull/1950 (which is supposed to fix this), it should use the new property probably?!
Or is there another delegation that should be added?

It is not rare if you move windows around in a tiling window manager.

While pytest is still running? I know it can happen, but do we really need to update the terminal width in those situations?

Regardless, is it too costly to make terminal_width a property which always calls get_terminal_width()? This way it will always be up-to-date. I don't think config.terminal_width will be called a lot of times, only in a few specialized places.

While pytest is still running? I know it can happen, but do we really need to update the terminal width in those situations?

Yes. It is especially important with pytest-sugar, which uses this property (fullwidth) to align its output.

I also think that using a property is the way to go. See https://github.com/pytest-dev/py/pull/92#issue-185417997. Ideally this would be cached until the next WINCH signal - but there appears to be no handling for this yet.

I can confirm I regularely resize pytest's window size while it's running, and pytest-sugar's behavior as well as pytest core's (having a way too long === line) are mildly annoying :wink:

I can confirm I regularely resize pytest's window size while it's running

My mistake then. 馃榿

I also think that using a property is the way to go.

Agree. Can we get away with not caching it? Is that call expensive?

@nicoddemus the problem is not the call, but the fact that it breaks in certain situations (like xdist slave formatting reports)

a more "correct" solution would be to defer such formatting until hitting the actual user interface
but that is a massive refactoring we cant easily undertake in reasonable time

a more "correct" solution would be to defer such formatting until hitting the actual user interface
but that is a massive refactoring we cant easily undertake in reasonable time

Since we don't have time for that refactoring now, would using a property be a "good enough" solution for now?

@nicoddemus yes, see pytest-dev/py#92

Great, we're all in agreement then! 馃槃

pytest-dev/py#92 got merged we need a followup

closing this one as we simply created a mess due to lacks in pylib
pylib should expose the primitives correctly instead of hardcoding the file id

While waiting for the fix in pytest, there's a workaround if you are running pytest as subprocess and piping stdout. The following code gets the terminal width and sets it as an environment variable.

import subprocess
p=subprocess.run(["stty","size"],stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
row, col= p.stdout.decode('utf-8').strip().split()
os.environ["COLUMNS"]=col

#further code to run pytest as a subprocess. 

@jeremyarr
Not sure how this helps, since it won't update it dynamically (when resizing the terminal window).
And $COLUMNS is set from the shell normally already.

@blueyed The above code is specifically for the use case of running pytest as a subprocess with stdout piped, which doesn't pass $COLUMNS from the shell. There's no issue if not piping stdout. But yes, it won't update it if the terminal window is resized mid call.

sorry to ask a support question in an issue thread, but is there a good way to find the current real terminal width? assuming i'm being naughty and don't care about resizes during test runs? it's for an assertrepr_compare plugin i'm working on...

@hjwp, py.io.TerminalWriter.full_width is how pytest itself obtains it:

>>> import py
>>> py.io.TerminalWriter().fullwidth
100

cool! that works. as long as I do it outside of my hook function, at import-time of my plugin. thanks Bruno

Was this page helpful?
0 / 5 - 0 ratings