-
Notifications
You must be signed in to change notification settings - Fork 192
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
Initial version of auto-detection of terminal width. #110
base: master
Are you sure you want to change the base?
Conversation
Update: it seems that the behaviour of |
Thanks for contributing! IMO it's not worth spending significant time on perfecting 2.7 since it's well past EOL. I haven't looked carefully but it sounds like you've made a significant improvement for 3.x and not made anything worse for 2.7 and that's great. You can write something like |
You're very welcome! I love your icecream package and am happy to give back.
That's awesome thanks. Unfortunately I had to skip the entire test because there's an assertion inside But the test is committed, I'll mark this PR as ready. |
icecream/icecream.py
Outdated
# TODO come up with a more elegant solution than subtracting | ||
# a seemingly random number of characters. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Any thoughts about this overflow thingy? I just realised that the extra characters might come from the ic |
prefix.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's probably what it is, yes. Have this function return the full width, then deal with the length of the prefix inside the class. Remember there's both the ic |
prefix (which can be configured) as well as the argPrefix
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me know what you think of my new solution.
icecream/icecream.py
Outdated
s = s.replace('\\n', '\n') # Preserve string newlines in output. | ||
return s | ||
|
||
|
||
def columns(): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please give this a more descriptive name.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
icecream/icecream.py
Outdated
width = os.get_terminal_size().columns - COLUMN_OVERFLOW | ||
except OSError: # Not in TTY | ||
pass | ||
except AttributeError: # Python 2.x |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks a bit too broad and prone to hiding real errors. I'd prefer either checking the Python version or checking for the presence of the relevant attribute.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
icecream/icecream.py
Outdated
try: | ||
# TODO come up with a more elegant solution than subtracting | ||
# a seemingly random number of characters. | ||
width = os.get_terminal_size().columns - COLUMN_OVERFLOW |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The docs https://docs.python.org/3/library/os.html#os.get_terminal_size suggest using shutil (https://docs.python.org/3/library/shutil.html#shutil.get_terminal_size) instead, and looking at that function I think that's a good idea.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall I like the approach and I appreciate the PR. The supports_param
idea is clever.
@alexmojaki thanks for the review! I've tried to implement your suggestions, which actually led to a different implementation of |
icecream/icecream.py
Outdated
""" Returns the number of columns that this terminal can handle. """ | ||
width = default | ||
try: | ||
if hasattr(shutil, "get_terminal_size"): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer to have near the top of the file:
try:
from shutil import get_terminal_size
except ImportError:
try:
from backports.shutil_get_terminal_size import get_terminal_size
except ImportError:
def get_terminal_size():
# get COLUMNS environment variable
icecream should still work if backports.shutil_get_terminal_size
isn't installed, e.g. if the user chooses to uninstall it even after installing it automatically.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do it that way then 👍
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Although in my terminal, env['COLUMNS']
isn't set, so I'm not sure which environments/OSes do set that variable)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know anything about this either, but I imagine it's a convention so that humans can easily set COLUMNS=50 when running a program and expect that many parts of the program will automatically respect that.
icecream/icecream.py
Outdated
else: # Python 2.x doesn't support get_terminal_size | ||
from backports.shutil_get_terminal_size import get_terminal_size | ||
width = get_terminal_size().columns | ||
except OSError: # Not in TTY |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like the shutil version doesn't raise OSError any more. But maybe the whole thing should be wrapped in except Exception
just in case something weird happens.
Also you should pass the default width as a fallback to get_terminal_size
. Otherwise it's going to use its own default fallback (80 columns) when the other methods fail.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do on both counts.
icecream/icecream.py
Outdated
@@ -173,6 +197,8 @@ def __init__(self, prefix=DEFAULT_PREFIX, | |||
self.includeContext = includeContext | |||
self.outputFunction = outputFunction | |||
self.argToStringFunction = argToStringFunction | |||
self.passWidthParam = supports_param(self.argToStringFunction) | |||
self.lineWrapWidth = detect_terminal_width(self.prefix, DEFAULT_LINE_WRAP_WIDTH) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This still doesn't account for the width of argPrefix
, or changing the prefix with configureOutput
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, will fix.
Co-authored-by: Alex Hall <[email protected]>
Co-authored-by: Alex Hall <[email protected]>
Started to make some suggestions then I realised the |
Co-authored-by: Alex Hall <[email protected]>
Hi @alexmojaki , sorry for the delay, work has been very busy. I have been thinking that it might be best not to enable this feature by default, at least in its first release, otherwise it could introduce breaking changes for a lot of people. So I have added some code to leave it off by default. There's now also at least one test that tests it by using a NB. I'm also a little bemused by the current behaviour of my new ic("banana") should print: ic| 'banana' When I manually run a Python script with the above code, it behaves as expected. But in my test, it actually appears to print the name of the literal along with its value: ic| "banana banana": ('banana '
'banana') Any idea what's going on? |
I think if the experience is generally improved then it should be enabled by default. Most people aren't going to even know they can configure this. As for breaking changes, this is a 'casual' debugging tool, I don't think there are significant expectations or guarantees of backward compatibility. It's hard to picture how this would significantly affect people negatively. If we want to be really careful, we could release a new major version. @gruns what do you think?
This is a bug first noticed in #84 (comment) Basically the source of the literal argument is still printed when the value is multiline, but it shouldn't. |
The main problem is that you still need to account for the other parts of the output, and it's quite complicated. There's four pieces:
Each of these pieces needs to be added one at a time to see if they can fit within the terminal width. If not, that starts a new indented line with a new available width. Your code mentions both 'terminal width' and 'line wrap width' in more places than I think it should, and the distinction is confusing. You should only need to store the attribute |
Co-authored-by: Alex Hall <[email protected]>
Thank you, this is the context I needed. I think I've catered for the prefix, but definitely not for anything else. I suspect that a lot more digging is needed for me to get familiar with how/when 2, 3 and 4 are added to the output.
OK, will do. Also, based on your thoughts, I'll refactor to remove the "disable by default". It'll be on for everyone. Let's just hope nobody is basing important logic on the output of an informal debugging tool 😉 I'm not sure I picked the simplest task for my first contribution 😂 😬 BTW, thank you so much for sticking with me on this 🙏 |
Might fix #90.
shutil.get_terminal_size
to detect terminal size in Python 3backports.shutil_get_terminal_size
to detect terminal size in Python 2, as per this SO answer. This appears to be more effective than the suggested approach of checkingos.env['COLUMNS']
but does come at the price of a new dependency. The various answers in the SO link make an interesting read.argToStringFunction
, even if a custom function is being used instead of the defaultpprint
, but only if the custom function supports awidth
parameter.lineWrapWidth
is being reset before the start of every test, for idempotency.Notes
pprint
in Python 2 doesn't wrap long strings so we test slightly different behaviour between Python 2 and Python 3.OSError
and abandon the attempt at detecting line widthtox
against any other environments or in any other OSes.