-
-
Notifications
You must be signed in to change notification settings - Fork 116
CLI eats my cursor #433
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
Comments
I can confirm. This only happens with sqlite-utils. I am using gnome-terminal with bash. |
Marking this as help wanted because I can't figure out how to replicate it! |
When I run https://linux.die.net/man/1/reset
|
Came here to say that I also have this issue. |
I think I see what is happening here, although I haven't quite work out a fix yet. Usually:
(See terminal escape sequences However the sqlite-utils @contextlib.contextmanager
def file_progress(file, silent=False, **kwargs):
...
with click.progressbar(length=file_length, **kwargs) as bar:
yield UpdateWrapper(file, bar.update) The yielded |
I also ran into this recently. See below for a patch for one possible solution (tested via "it works on my machine", but I don't expect that this behavior would vary a whole lot across terminal emulators and shells). Another possible solution might be to subclass click's diff --git a/sqlite_utils/utils.py b/sqlite_utils/utils.py
index 06c1a4c..530a3a3 100644
--- a/sqlite_utils/utils.py
+++ b/sqlite_utils/utils.py
@@ -147,14 +147,23 @@ def decode_base64_values(doc):
class UpdateWrapper:
- def __init__(self, wrapped, update):
+ def __init__(self, wrapped, update, render_finish):
self._wrapped = wrapped
self._update = update
+ self._render_finish = render_finish
def __iter__(self):
- for line in self._wrapped:
- self._update(len(line))
- yield line
+ return self
+
+ def __next__(self):
+ try:
+ line = next(self._wrapped)
+ except StopIteration as e:
+ self._render_finish()
+ raise
+
+ self._update(len(line))
+ return line
def read(self, size=-1):
data = self._wrapped.read(size)
@@ -178,7 +187,7 @@ def file_progress(file, silent=False, **kwargs):
else:
file_length = os.path.getsize(file.name)
with click.progressbar(length=file_length, **kwargs) as bar:
- yield UpdateWrapper(file, bar.update)
+ yield UpdateWrapper(file, bar.update, bar.render_finish)
class Format(enum.Enum):
|
Came here to report this, but instead I'll confirm the issue across two terminal emulators (Gnome Terminal and Alacritty) on Pop_OS! 22.04 (currently based on Ubuntu/Gnome). Also messes up the formatting of the terminal. Can also confirm that reset fixes it until the next sqlite-utils command. |
I confirm the bug, as above, and that @jonafato 's patch fixes it for me. However, it's not the right fix. The problem is that ProgressBar is being used in the wrong way. This also results in two lines being printed instead of one, like this:
The bug is reproducible for me in any terminal, including Gnome Terminal and Guake, and VSCode. With VSCode I can use this launch.json to reproduce it: {
"version": "0.2.0",
"configurations": [
{
"name": "Python: Module",
"type": "python",
"request": "launch",
"module": "sqlite_utils",
"justMyCode": false,
"args": ["insert", "test.db", "test", "--csv", "tests/sniff/example1.csv"]
}
]
} [edit - deleted my analysis of why the current code is wrong, which was confused and confusing] |
The issue is that underlying iterator is not fully consumed within the body of the `with file_progress()` block. Instead, that block creates generator expressions like `docs = (dict(zip(headers, row)) for row in reader)` These iterables are consumed later, outside the `with file_progress()` block, which consumes the underlying iterator, and in turn updates the progress bar. This means that the `ProgressBar.__exit__` method gets called before the last time the `ProgressBar.update` method gets called. The result is that the code to make the cursor invisible (inside the `update()` method) is called after the cleanup code to make it visible (in the `__exit__` method). The fix is to move consumption of the `docs` iterators within the progress bar block. (An additional fix, to make ProgressBar more robust against this kind of misuse, would to make it refusing to update after its `__exit__` method had been called, just like files cannot be `read()` after they are closed. That requires a in the click library).
The issue is that underlying iterator is not fully consumed within the body of the `with file_progress()` block. Instead, that block creates generator expressions like `docs = (dict(zip(headers, row)) for row in reader)` These iterables are consumed later, outside the `with file_progress()` block, which consumes the underlying iterator, and in turn updates the progress bar. This means that the `ProgressBar.__exit__` method gets called before the last time the `ProgressBar.update` method gets called. The result is that the code to make the cursor invisible (inside the `update()` method) is called after the cleanup code to make it visible (in the `__exit__` method). The fix is to move consumption of the `docs` iterators within the progress bar block. (An additional fix, to make ProgressBar more robust against this kind of misuse, would to make it refusing to update after its `__exit__` method had been called, just like files cannot be `read()` after they are closed. That requires a in the click library).
And a GIF of the fix after applying: |
I'm not sure why this happens but
sqlite-utils
makes my terminal cursor disappear after running commands likesqlite-utils insert
. I've only noticed this behavior insqlite-utils
, not in any other CLI toolsI can still type commands after it runs but the text cursor is invisible
The text was updated successfully, but these errors were encountered: