Skip to content

Commit

Permalink
Add a 'y' command to copy focused query to clipboard
Browse files Browse the repository at this point in the history
  • Loading branch information
dlax committed May 28, 2024
1 parent e5dd8ed commit d1bece3
Show file tree
Hide file tree
Showing 10 changed files with 143 additions and 0 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
* Add non-negative counterparts of many `--no-...` command-line option, thus
allowing to enable respective feature/behaviour even if disabled in the
configuration. (Requires Python 3.9 or higher.)
* Add a `y` command to copy focused query to the clipboard, using
[pyperclip](https://pypi.org/project/pyperclip/) (#311).

### Fixed

Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ bytes). If your SQL query text look truncated, you should increase
| `c` | Sort by CPU%, descending |
| `m` | Sort by MEM%, descending |
| `t` | Sort by TIME+, descending |
| `y` | Copy focused query to clipboard |
| `T` | Change duration mode: query, transaction, backend |
| `Space` | Pause on/off |
| `v` | Change queries display mode: full, indented, truncated |
Expand Down
2 changes: 2 additions & 0 deletions docs/man/pg_activity.1
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,8 @@ See: https://www.postgresql.org/docs/current/libpq\-envars.html
.IX Item "m Sort by MEM%, descending."
.IP "\fBt\fR Sort by \s-1TIME+,\s0 descending." 2
.IX Item "t Sort by TIME+, descending."
.IP "\fBy\fR Copy focused query to clipboard." 2
.IX Item "y Copy focused query to clipboard."
.IP "\fBT\fR Change duration mode: query, transaction, backend." 2
.IX Item "T Change duration mode: query, transaction, backend."
.IP "\fBSpace\fR Pause on/off." 2
Expand Down
2 changes: 2 additions & 0 deletions docs/man/pg_activity.pod
Original file line number Diff line number Diff line change
Expand Up @@ -417,6 +417,8 @@ See: https://www.postgresql.org/docs/current/libpq-envars.html

=item B<t> Sort by TIME+, descending.

=item B<y> Copy focused query to clipboard.

=item B<T> Change duration mode: query, transaction, backend.

=item B<Space> Pause on/off.
Expand Down
3 changes: 3 additions & 0 deletions mypy.ini
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@
files = pgactivity
show_error_codes = true
strict = true

[mypy-pyperclip]
ignore_missing_imports = true
1 change: 1 addition & 0 deletions pgactivity/keys.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ def __eq__(self, other: Any) -> bool:
EXIT = "q"
HELP = "h"
SPACE = " "
COPY_TO_CLIPBOARD = "y"
PROCESS_CANCEL = "C"
PROCESS_KILL = "K"
PROCESS_FIRST = "KEY_HOME"
Expand Down
14 changes: 14 additions & 0 deletions pgactivity/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import attr
import psutil
import pyperclip
from attr import validators

from . import colors, compat, pg, utils
Expand Down Expand Up @@ -1168,6 +1169,19 @@ def toggle_pin_focused(self) -> None:
except KeyError:
self.pinned.add(self.focused)

def copy_focused_query_to_clipboard(self) -> str:
assert self.focused is not None
for proc in self.items:
if proc.pid == self.focused:
break
else:
return "no focused process found"
if proc.query is None:
return "process has no query"
else:
pyperclip.copy(proc.query)
return f"query of process {proc.pid} copied to clipboard"


ActivityStats = Union[
Iterable[WaitingProcess],
Expand Down
3 changes: 3 additions & 0 deletions pgactivity/ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ def main(
ui.start_interactive()
elif key == keys.SPACE:
pg_procs.toggle_pin_focused()
elif key == keys.COPY_TO_CLIPBOARD:
msg = pg_procs.copy_focused_query_to_clipboard()
msg_pile.send(msg)
elif key.name == keys.CANCEL_SELECTION:
pg_procs.reset()
ui.end_interactive()
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ dependencies = [
"humanize >= 0.5.1",
"importlib_metadata; python_version < '3.8'",
"psutil >= 2.0.0",
"pyperclip",
]

[project.optional-dependencies]
Expand Down
114 changes: 114 additions & 0 deletions tests/test_ui.txt
Original file line number Diff line number Diff line change
Expand Up @@ -951,6 +951,120 @@ Interactive mode:
(Note: we patch boxed() widget to disable border in order to make output
independent of the number of digits in PIDs.)

>>> keys = ["j", "j", "y", "q"]
>>> with patch.object(
... widgets, "boxed", new=functools.partial(widgets.boxed, border=False),
... ):
... run_ui(options, keys, render_footer=True, render_header=False, width=140) # doctest: +ELLIPSIS,+NORMALIZE_WHITESPACE
RUNNING QUERIES
PID DATABASE state Query
... tests idle in trans SELECT 42
... tests idle in trans SELECT 43
... tests idle in trans UPDATE t SET s = 'blocking'
... tests active UPDATE t SET s = 'waiting'
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
F1/1 Running queries F2/2 Waiting queries F3/3 Blocking queries Space Pause/unpause q Quit h Help
------------------------------------------------------------- sending key 'j' --------------------------------------------------------------
RUNNING QUERIES
PID DATABASE state Query
... tests idle in trans SELECT 42
... tests idle in trans SELECT 43
... tests idle in trans UPDATE t SET s = 'blocking'
... tests active UPDATE t SET s = 'waiting'
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
C Cancel current query K Terminate underlying ses Space Tag/untag current qu Other Back to activities q Quit
------------------------------------------------------------- sending key 'j' --------------------------------------------------------------
RUNNING QUERIES
PID DATABASE state Query
... tests idle in trans SELECT 43
... tests idle in trans UPDATE t SET s = 'blocking'
... tests active UPDATE t SET s = 'waiting'
... tests idle in trans SELECT 42
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
C Cancel current query K Terminate underlying ses Space Tag/untag current qu Other Back to activities q Quit
------------------------------------------------------------- sending key 'y' --------------------------------------------------------------
RUNNING QUERIES
PID DATABASE state Query
... tests idle in trans SELECT 43
... tests idle in trans UPDATE t SET s = 'blocking'
... tests active UPDATE t SET s = 'waiting'
... tests idle in trans SELECT 42
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
<BLANKLINE>
query of process ... copied to clipboard
------------------------------------------------------------- sending key 'q' --------------------------------------------------------------

>>> import pyperclip
>>> pyperclip.paste()
'SELECT 43'

>>> key_down = {"ucs": "KEY_DOWN", "name": "KEY_DOWN"}
>>> keys = [key_down, 2, 3, 1, key_down, "C", "n", "K", "y",
... key_down, " ", "j", " ", "K", "y", "q"]
Expand Down

0 comments on commit d1bece3

Please sign in to comment.