visidata 3.1.1__py3-none-any.whl → 3.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- visidata/__init__.py +2 -2
- visidata/_input.py +106 -58
- visidata/_open.py +10 -7
- visidata/_types.py +2 -2
- visidata/aggregators.py +125 -16
- visidata/apps/vdsql/_ibis.py +8 -13
- visidata/basesheet.py +4 -3
- visidata/canvas.py +11 -7
- visidata/clipboard.py +11 -2
- visidata/cliptext.py +68 -23
- visidata/cmdlog.py +5 -1
- visidata/column.py +48 -33
- visidata/ddwplay.py +2 -2
- visidata/deprecated.py +96 -63
- visidata/errors.py +41 -5
- visidata/{features → experimental}/helloworld.py +1 -1
- visidata/experimental/liveupdate.py +1 -1
- visidata/expr.py +1 -0
- visidata/extensible.py +4 -0
- visidata/features/cmdpalette.py +64 -25
- visidata/features/describe.py +2 -2
- visidata/features/expand_cols.py +7 -5
- visidata/features/freeze.py +14 -2
- visidata/features/go_col.py +3 -3
- visidata/features/graph_zoom_y.py +47 -0
- visidata/features/incr.py +7 -3
- visidata/features/join.py +23 -12
- visidata/features/layout.py +8 -4
- visidata/features/melt.py +1 -0
- visidata/features/rank.py +103 -0
- visidata/features/reload_every.py +11 -8
- visidata/features/sysedit.py +14 -4
- visidata/features/transpose.py +1 -0
- visidata/features/window.py +12 -0
- visidata/form.py +10 -9
- visidata/freqtbl.py +47 -3
- visidata/fuzzymatch.py +11 -7
- visidata/graph.py +5 -3
- visidata/guides/AggregatorsSheet.md +84 -0
- visidata/guides/CommandsSheet.md +1 -0
- visidata/guides/MacrosSheet.md +1 -1
- visidata/guides/RankGuide.md +51 -0
- visidata/guides/TypesSheet.md +1 -1
- visidata/guides/WindowFunctionGuide.md +49 -0
- visidata/help.py +23 -6
- visidata/indexsheet.py +1 -1
- visidata/loaders/_pandas.py +3 -1
- visidata/loaders/archive.py +33 -6
- visidata/loaders/csv.py +12 -1
- visidata/loaders/eml.py +2 -0
- visidata/loaders/f5log.py +2 -2
- visidata/loaders/fec.py +6 -9
- visidata/loaders/fixed_width.py +2 -0
- visidata/loaders/hdf5.py +34 -10
- visidata/loaders/npy.py +54 -23
- visidata/loaders/orgmode.py +3 -2
- visidata/loaders/pandas_freqtbl.py +4 -0
- visidata/loaders/psv.py +13 -0
- visidata/loaders/sqlite.py +1 -1
- visidata/loaders/vds.py +3 -4
- visidata/macros.py +5 -4
- visidata/main.py +21 -11
- visidata/mainloop.py +8 -5
- visidata/man/parse_options.py +3 -2
- visidata/man/vd.1 +38 -17
- visidata/man/vd.txt +47 -17
- visidata/menu.py +10 -10
- visidata/metasheets.py +3 -3
- visidata/mouse.py +3 -0
- visidata/movement.py +6 -3
- visidata/pyobj.py +17 -9
- visidata/save.py +10 -2
- visidata/selection.py +29 -18
- visidata/settings.py +9 -5
- visidata/sheets.py +124 -48
- visidata/shell.py +2 -2
- visidata/sidebar.py +11 -8
- visidata/sort.py +89 -11
- visidata/statusbar.py +10 -9
- visidata/tests/test_cliptext.py +164 -0
- visidata/tests/test_commands.py +6 -2
- visidata/tests/test_menu.py +1 -1
- visidata/textsheet.py +34 -8
- visidata/themes/ascii8.py +2 -2
- visidata/themes/light.py +5 -0
- visidata/threads.py +38 -8
- visidata/utils.py +15 -1
- visidata/vendor/__init__.py +0 -0
- {visidata-3.1.1.data → visidata-3.3.data}/data/share/man/man1/vd.1 +38 -17
- {visidata-3.1.1.data → visidata-3.3.data}/data/share/man/man1/visidata.1 +38 -17
- {visidata-3.1.1.dist-info → visidata-3.3.dist-info}/METADATA +62 -15
- {visidata-3.1.1.dist-info → visidata-3.3.dist-info}/RECORD +98 -92
- {visidata-3.1.1.dist-info → visidata-3.3.dist-info}/WHEEL +1 -1
- {visidata-3.1.1.dist-info → visidata-3.3.dist-info}/entry_points.txt +1 -0
- visidata-3.1.1.data/scripts/vd +0 -6
- {visidata-3.1.1.data → visidata-3.3.data}/data/share/applications/visidata.desktop +0 -0
- {visidata-3.1.1.data → visidata-3.3.data}/scripts/vd2to3.vdx +0 -0
- {visidata-3.1.1.dist-info → visidata-3.3.dist-info}/LICENSE.gpl3 +0 -0
- {visidata-3.1.1.dist-info → visidata-3.3.dist-info}/top_level.txt +0 -0
visidata/deprecated.py
CHANGED
@@ -3,12 +3,16 @@ import functools
|
|
3
3
|
from visidata import VisiData, vd
|
4
4
|
import visidata
|
5
5
|
|
6
|
-
|
6
|
+
def deprecated_alias(depver, *args, **kwargs):
|
7
|
+
# expand this to create cmd
|
8
|
+
# with cmd.deprecated=depver
|
9
|
+
return visidata.BaseSheet.bindkey(*args, **kwargs)
|
7
10
|
|
8
|
-
|
11
|
+
@VisiData.api
|
12
|
+
def deprecated_warn(vd, funcname, ver, instead):
|
9
13
|
import traceback
|
10
14
|
|
11
|
-
msg = f'{
|
15
|
+
msg = f'{funcname} deprecated since v{ver}'
|
12
16
|
if instead:
|
13
17
|
msg += f'; use {instead}'
|
14
18
|
|
@@ -20,18 +24,27 @@ def deprecated_warn(func, ver, instead):
|
|
20
24
|
vd.warning(f'Deprecated call traceback (most recent last):')
|
21
25
|
|
22
26
|
|
23
|
-
def deprecated(ver, instead=''):
|
27
|
+
def deprecated(ver, instead='', check=True):
|
24
28
|
def decorator(func):
|
25
29
|
@functools.wraps(func)
|
26
30
|
def wrapper(*args, **kwargs):
|
27
|
-
deprecated_warn(
|
31
|
+
vd.deprecated_warn(wrapper.__name__, ver, instead)
|
28
32
|
return func(*args, **kwargs)
|
33
|
+
|
34
|
+
if check and hasattr(func, '_extensible_api'):
|
35
|
+
vd.error(f"{func.__name__}: @deprecated applied in wrong order") #2623
|
36
|
+
|
29
37
|
return wrapper
|
30
38
|
return decorator
|
31
39
|
|
32
40
|
|
33
|
-
|
41
|
+
def _deprecated_api(ver, instead=''):
|
42
|
+
'Decorator to deliberately wrap non-deprecated .api functions as a deprecated global function. Use @deprecated instead, except in deprecated.py.'
|
43
|
+
return deprecated(ver, instead, check=False)
|
44
|
+
|
45
|
+
|
34
46
|
@VisiData.api
|
47
|
+
@deprecated('1.6', 'vd instead of vd()')
|
35
48
|
def __call__(vd):
|
36
49
|
'Deprecated; use plain "vd"'
|
37
50
|
return vd
|
@@ -39,7 +52,7 @@ def __call__(vd):
|
|
39
52
|
|
40
53
|
@deprecated('1.6')
|
41
54
|
def copyToClipboard(value):
|
42
|
-
vd.error("copyToClipboard longer implemented")
|
55
|
+
vd.error("copyToClipboard no longer implemented")
|
43
56
|
return visidata.clipboard_copy(value)
|
44
57
|
|
45
58
|
|
@@ -62,32 +75,32 @@ def bindkey_override(keystrokes, longname):
|
|
62
75
|
bindkey = visidata.BaseSheet.bindkey
|
63
76
|
unbindkey = visidata.BaseSheet.unbindkey
|
64
77
|
|
65
|
-
@deprecated('2.0')
|
66
78
|
@visidata.Sheet.api
|
79
|
+
@deprecated('2.0')
|
67
80
|
def exec_keystrokes(self, keystrokes, vdglobals=None):
|
68
81
|
return self.execCommand(self.getCommand(keystrokes), vdglobals, keystrokes=keystrokes)
|
69
82
|
|
70
83
|
visidata.Sheet.exec_command = deprecated('2.0')(visidata.Sheet.execCommand)
|
71
84
|
|
72
|
-
@deprecated('2.0', 'def open_<filetype> instead')
|
73
85
|
@VisiData.api
|
86
|
+
@deprecated('2.0', 'def open_<filetype> instead')
|
74
87
|
def filetype(vd, ext, constructor):
|
75
88
|
'Add constructor to handle the given file type/extension.'
|
76
89
|
globals().setdefault('open_'+ext, lambda p,ext=ext: constructor(p.base_stem, source=p, filetype=ext))
|
77
90
|
|
78
|
-
@deprecated('2.0', 'Sheet(namepart1, namepart2, ...)')
|
79
91
|
@VisiData.global_api
|
92
|
+
@deprecated('2.0', 'Sheet(namepart1, namepart2, ...)')
|
80
93
|
def joinSheetnames(vd, *sheetnames):
|
81
94
|
'Concatenate sheet names in a standard way'
|
82
95
|
return visidata.options.name_joiner.join(str(x) for x in sheetnames)
|
83
96
|
|
84
|
-
@deprecated('2.0', 'PyobjSheet')
|
85
97
|
@VisiData.global_api
|
98
|
+
@deprecated('2.0', 'PyobjSheet')
|
86
99
|
def load_pyobj(*names, **kwargs):
|
87
100
|
return visidata.PyobjSheet(*names, **kwargs)
|
88
101
|
|
89
|
-
@deprecated('2.0', 'PyobjSheet')
|
90
102
|
@VisiData.global_api
|
103
|
+
@deprecated('2.0', 'PyobjSheet')
|
91
104
|
def push_pyobj(name, pyobj):
|
92
105
|
vs = visidata.PyobjSheet(name, source=pyobj)
|
93
106
|
if vs:
|
@@ -103,33 +116,33 @@ visidata.addGlobals({'load_pyobj': load_pyobj, 'isNumeric': isNumeric})
|
|
103
116
|
|
104
117
|
# The longnames on the left are deprecated for 2.0
|
105
118
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
119
|
+
deprecated_alias('2.0', 'edit-cells', 'setcol-input')
|
120
|
+
deprecated_alias('2.0', 'fill-nulls', 'setcol-fill')
|
121
|
+
deprecated_alias('2.0', 'paste-cells', 'setcol-clipboard')
|
122
|
+
deprecated_alias('2.0', 'frequency-rows', 'frequency-summary')
|
123
|
+
deprecated_alias('2.0', 'dup-cell', 'dive-cell')
|
124
|
+
deprecated_alias('2.0', 'dup-row', 'dive-row')
|
125
|
+
deprecated_alias('2.0', 'next-search', 'search-next')
|
126
|
+
deprecated_alias('2.0', 'prev-search', 'search-prev')
|
127
|
+
deprecated_alias('2.0', 'search-prev', 'searchr-next')
|
128
|
+
deprecated_alias('2.0', 'prev-sheet', 'jump-prev')
|
129
|
+
deprecated_alias('2.0', 'prev-value', 'go-prev-value')
|
130
|
+
deprecated_alias('2.0', 'next-value', 'go-next-value')
|
131
|
+
deprecated_alias('2.0', 'prev-selected', 'go-prev-selected')
|
132
|
+
deprecated_alias('2.0', 'next-selected', 'go-next-selected')
|
133
|
+
deprecated_alias('2.0', 'prev-null', 'go-prev-null')
|
134
|
+
deprecated_alias('2.0', 'next-null', 'go-next-null')
|
135
|
+
deprecated_alias('2.0', 'page-right', 'go-right-page')
|
136
|
+
deprecated_alias('2.0', 'page-left', 'go-left-page')
|
137
|
+
deprecated_alias('2.0', 'dive-cell', 'open-cell')
|
138
|
+
deprecated_alias('2.0', 'dive-row', 'open-row')
|
139
|
+
deprecated_alias('2.0', 'add-sheet', 'open-new')
|
140
|
+
deprecated_alias('2.0', 'save-sheets-selected', 'save-selected')
|
141
|
+
deprecated_alias('2.0', 'join-sheets', 'join-selected')
|
142
|
+
deprecated_alias('2.0', 'dive-rows', 'dive-selected')
|
130
143
|
|
131
144
|
# v2.3
|
132
|
-
|
145
|
+
deprecated_alias('2.3', 'show-aggregate', 'memo-aggregate')
|
133
146
|
#theme('use_default_colors', True, 'curses use default terminal colors')
|
134
147
|
#option('expand_col_scanrows', 1000, 'number of rows to check when expanding columns (0 = all)')
|
135
148
|
|
@@ -149,25 +162,25 @@ def load_tsv(fn):
|
|
149
162
|
|
150
163
|
# NOTE: you cannot use deprecated() with nonfuncs
|
151
164
|
|
152
|
-
cancelThread =
|
153
|
-
status =
|
154
|
-
warning =
|
155
|
-
error =
|
156
|
-
debug =
|
157
|
-
fail =
|
165
|
+
cancelThread = _deprecated_api('2.6', 'vd.cancelThread')(vd.cancelThread)
|
166
|
+
status = _deprecated_api('2.6', 'vd.status')(vd.status)
|
167
|
+
warning = _deprecated_api('2.6', 'vd.warning')(vd.warning)
|
168
|
+
error = _deprecated_api('2.6', 'vd.error')(vd.error)
|
169
|
+
debug = _deprecated_api('2.6', 'vd.debug')(vd.debug)
|
170
|
+
fail = _deprecated_api('2.6', 'vd.fail')(vd.fail)
|
158
171
|
|
159
172
|
option = theme = vd.option # deprecated('2.6', 'vd.option')(vd.option)
|
160
173
|
jointypes = vd.jointypes # deprecated('2.6', 'vd.jointypes')(vd.jointypes)
|
161
|
-
confirm =
|
162
|
-
launchExternalEditor =
|
163
|
-
launchEditor =
|
164
|
-
exceptionCaught =
|
165
|
-
openSource =
|
174
|
+
confirm = _deprecated_api('2.6', 'vd.confirm')(vd.confirm)
|
175
|
+
launchExternalEditor = _deprecated_api('2.6', 'vd.launchExternalEditor')(vd.launchExternalEditor)
|
176
|
+
launchEditor = _deprecated_api('2.6', 'vd.launchEditor')(vd.launchEditor)
|
177
|
+
exceptionCaught = _deprecated_api('2.6', 'vd.exceptionCaught')(vd.exceptionCaught)
|
178
|
+
openSource = _deprecated_api('2.6', 'vd.openSource')(vd.openSource)
|
166
179
|
globalCommand = visidata.BaseSheet.addCommand
|
167
|
-
visidata.Sheet.StaticColumn =
|
180
|
+
visidata.Sheet.StaticColumn = _deprecated_api('2.11', 'Sheet.freeze_col')(visidata.Sheet.freeze_col)
|
168
181
|
#visidata.Path.open_text = deprecated('3.0', 'visidata.Path.open')(visidata.Path.open) # undeprecated in 3.1
|
169
182
|
|
170
|
-
vd.sysclip_value =
|
183
|
+
vd.sysclip_value = _deprecated_api('3.0', 'vd.sysclipValue')(vd.sysclipValue)
|
171
184
|
|
172
185
|
def itemsetter(i):
|
173
186
|
def g(obj, v):
|
@@ -181,8 +194,8 @@ vd.optalias('confirm_overwrite', 'overwrite', 'confirm')
|
|
181
194
|
vd.optalias('show_graph_labels', 'disp_graph_labels')
|
182
195
|
vd.optalias('zoom_incr', 'disp_zoom_incr')
|
183
196
|
|
184
|
-
|
185
|
-
|
197
|
+
deprecated_alias('3.0', 'visibility-sheet', 'toggle-multiline')
|
198
|
+
deprecated_alias('3.0', 'visibility-col', 'toggle-multiline')
|
186
199
|
|
187
200
|
def clean_to_id(s):
|
188
201
|
return visidata.vd.cleanName(s)
|
@@ -204,7 +217,7 @@ class OnExit:
|
|
204
217
|
except Exception as e:
|
205
218
|
vd.exceptionCaught(e)
|
206
219
|
|
207
|
-
|
220
|
+
deprecated_alias('3.0', 'open-inputs', 'open-input-history')
|
208
221
|
|
209
222
|
#vd.option('plugins_url', 'https://visidata.org/plugins/plugins.jsonl', 'source of plugins sheet')
|
210
223
|
|
@@ -216,37 +229,57 @@ def inputRegexSubstOld(vd, prompt):
|
|
216
229
|
return dict(before=before, after=after)
|
217
230
|
|
218
231
|
|
219
|
-
visidata.Sheet.addCommand('', 'addcol-subst', 'addColumnAtCursor(Column(cursorCol.name + "_re", getter=regexTransform(cursorCol, **inputRegexSubstOld("transform column by regex: "))))', 'add column derived from current column, replacing regex with subst (may include \1 backrefs)', deprecated=
|
220
|
-
visidata.Sheet.addCommand('', 'setcol-subst', 'setValuesFromRegex([cursorCol], someSelectedRows, **inputRegexSubstOld("transform column by regex: "))', 'regex/subst - modify selected rows in current column, replacing regex with subst, (may include backreferences \\1 etc)', deprecated=
|
221
|
-
visidata.Sheet.addCommand('', 'setcol-subst-all', 'setValuesFromRegex(visibleCols, someSelectedRows, **inputRegexSubstOld(f"transform {nVisibleCols} columns by regex: "))', 'modify selected rows in all visible columns, replacing regex with subst (may include \\1 backrefs)', deprecated=
|
232
|
+
visidata.Sheet.addCommand('', 'addcol-subst', 'addColumnAtCursor(Column(cursorCol.name + "_re", getter=regexTransform(cursorCol, **inputRegexSubstOld("transform column by regex: "))))', 'add column derived from current column, replacing regex with subst (may include \1 backrefs)', deprecated='3.0')
|
233
|
+
visidata.Sheet.addCommand('', 'setcol-subst', 'setValuesFromRegex([cursorCol], someSelectedRows, **inputRegexSubstOld("transform column by regex: "))', 'regex/subst - modify selected rows in current column, replacing regex with subst, (may include backreferences \\1 etc)', deprecated='3.0')
|
234
|
+
visidata.Sheet.addCommand('', 'setcol-subst-all', 'setValuesFromRegex(visibleCols, someSelectedRows, **inputRegexSubstOld(f"transform {nVisibleCols} columns by regex: "))', 'modify selected rows in all visible columns, replacing regex with subst (may include \\1 backrefs)', deprecated='3.0')
|
222
235
|
|
223
|
-
visidata.Sheet.addCommand('', 'split-col', 'addRegexColumns(makeRegexSplitter, cursorCol, inputRegex("split regex: ", type="regex-split"))', 'Add new columns from regex split', deprecated=
|
224
|
-
visidata.Sheet.addCommand('', 'capture-col', 'addRegexColumns(makeRegexMatcher, cursorCol, inputRegex("capture regex: ", type="regex-capture"))', 'add new column from capture groups of regex; requires example row', deprecated=
|
236
|
+
visidata.Sheet.addCommand('', 'split-col', 'addRegexColumns(makeRegexSplitter, cursorCol, inputRegex("split regex: ", type="regex-split"))', 'Add new columns from regex split', deprecated='3.0')
|
237
|
+
visidata.Sheet.addCommand('', 'capture-col', 'addRegexColumns(makeRegexMatcher, cursorCol, inputRegex("capture regex: ", type="regex-capture"))', 'add new column from capture groups of regex; requires example row', deprecated='3.0')
|
225
238
|
|
226
239
|
#vd.option('cmdlog_histfile', '', 'file to autorecord each cmdlog action to', sheettype=None)
|
227
240
|
#BaseSheet.bindkey('KEY_BACKSPACE', 'menu-help')
|
228
241
|
|
229
|
-
@deprecated('3.0', 'vd.callNoExceptions(col.setValue, row, value)')
|
230
242
|
@visidata.Column.api
|
243
|
+
@deprecated('3.0', 'vd.callNoExceptions(col.setValue, row, value)')
|
231
244
|
def setValueSafe(self, row, value):
|
232
245
|
'setValue and ignore exceptions.'
|
233
246
|
return vd.callNoExceptions(self.setValue, row, value)
|
234
247
|
|
235
|
-
@deprecated('3.0', 'vd.callNoExceptions(sheet.checkCursor)')
|
236
248
|
@visidata.BaseSheet.api
|
249
|
+
@deprecated('3.0', 'vd.callNoExceptions(sheet.checkCursor)')
|
237
250
|
def checkCursorNoExceptions(sheet):
|
238
251
|
return vd.callNoExceptions(sheet.checkCursor)
|
239
252
|
|
240
|
-
@deprecated('3.1', 'vd.memoValue(name, value, displayvalue)')
|
241
253
|
@VisiData.api
|
254
|
+
@deprecated('3.1', 'vd.memoValue(name, value, displayvalue)')
|
242
255
|
def memo(vd, name, col, row):
|
243
256
|
return vd.memoValue(name, col.getTypedValue(row), col.getDisplayValue(row))
|
244
257
|
|
245
|
-
|
258
|
+
deprecated_alias('3.1', 'view-cell', 'pyobj-cell')
|
246
259
|
|
247
260
|
vd.optalias('textwrap_cells', 'disp_wrap_max_lines', 3) # wordwrap text for multiline rows
|
248
261
|
|
249
|
-
@deprecated('3.1', 'sheet.rowname(row)')
|
250
262
|
@visidata.TableSheet.api
|
263
|
+
@deprecated('3.1', 'sheet.rowname(row)')
|
251
264
|
def keystr(sheet, row):
|
252
265
|
return sheet.rowname(row)
|
266
|
+
|
267
|
+
vd.optalias('color_refline', 'color_graph_refline') # color_refline was used in v3.1 by mistake
|
268
|
+
|
269
|
+
@visidata.TableSheet.api
|
270
|
+
@deprecated('3.2', '[self.unsetKeys([c]) if c.keycol else self.setKeys([c]) for c in cols]')
|
271
|
+
def toggleKeys(self, cols):
|
272
|
+
for col in cols:
|
273
|
+
if col.keycol:
|
274
|
+
self.unsetKeys([col])
|
275
|
+
else:
|
276
|
+
self.setKeys([col])
|
277
|
+
|
278
|
+
vd.optalias('disp_pixel_random', 'disp_graph_pixel_random') #2661
|
279
|
+
|
280
|
+
vd.addGlobals(deprecated_warn=deprecated_warn)
|
281
|
+
|
282
|
+
# v3.3
|
283
|
+
|
284
|
+
#vd.option('disp_expert', 'max level of options and columns to include')
|
285
|
+
#vd.option('disp_help', '')
|
visidata/errors.py
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
import traceback
|
2
|
+
import sys
|
3
|
+
import re
|
2
4
|
|
3
5
|
from visidata import vd, VisiData
|
4
6
|
|
@@ -10,10 +12,43 @@ class ExpectedException(Exception):
|
|
10
12
|
pass
|
11
13
|
|
12
14
|
|
13
|
-
def stacktrace(e=None):
|
14
|
-
|
15
|
-
|
16
|
-
|
15
|
+
def stacktrace(e=None, exclude_caller=False):
|
16
|
+
'''Return a list of strings for the stack trace, without newlines
|
17
|
+
at the end. If an exception handler is executing, and *e* is none,
|
18
|
+
the stack trace includes extra levels of callers beyond the level
|
19
|
+
where the exception was caught. If *exclude_caller* is True, the
|
20
|
+
trace will exclude the function that called stacktrace(). The
|
21
|
+
trace will exclude several uninformative levels that are run
|
22
|
+
in interactive visidata.'''
|
23
|
+
|
24
|
+
if e:
|
25
|
+
return traceback.format_exception_only(type(e), e)
|
26
|
+
#in Python 3.11 we can replace sys.exc_info() with sys.exception()
|
27
|
+
handling = (sys.exc_info() != (None, None, None))
|
28
|
+
|
29
|
+
stack = ''.join(traceback.format_stack()).strip().splitlines()
|
30
|
+
|
31
|
+
if handling:
|
32
|
+
trim_levels = 2 # remove levels for stacktrace() -> format_stack()
|
33
|
+
if exclude_caller:
|
34
|
+
trim_levels += 1
|
35
|
+
trace_above = stack[:-2*trim_levels]
|
36
|
+
else:
|
37
|
+
trace_above = stack
|
38
|
+
if trace_above:
|
39
|
+
trace_above[0] = ' ' + trace_above[0] #fix indent level of first line
|
40
|
+
try:
|
41
|
+
# remove several levels of uninformative stacktrace in typical interactive vd
|
42
|
+
idx = trace_above.index(' ret = vd.mainloop(scr)')
|
43
|
+
trace_above = trace_above[idx+1:]
|
44
|
+
except ValueError:
|
45
|
+
pass
|
46
|
+
if not handling:
|
47
|
+
return trace_above
|
48
|
+
# remove lines that mark error columns with carets and sometimes tildes
|
49
|
+
trace_below = [ line for line in traceback.format_exc().strip().splitlines() if not re.match('^ *~*\\^+$', line) ]
|
50
|
+
# move the "Traceback (most recent call last) header to the top of the output
|
51
|
+
return [trace_below[0]] + trace_above + trace_below[1:]
|
17
52
|
|
18
53
|
|
19
54
|
@VisiData.api
|
@@ -21,7 +56,8 @@ def exceptionCaught(vd, exc=None, status=True, **kwargs):
|
|
21
56
|
'Add *exc* to list of last errors and add to status history. Show on left status bar if *status* is True. Reraise exception if options.debug is True.'
|
22
57
|
if isinstance(exc, ExpectedException): # already reported, don't log
|
23
58
|
return
|
24
|
-
|
59
|
+
# save a stack trace that does not include this function
|
60
|
+
vd.lastErrors.append(stacktrace(exclude_caller=True))
|
25
61
|
if status:
|
26
62
|
vd.status(f'{type(exc).__name__}: {exc}', priority=2)
|
27
63
|
else:
|
@@ -7,4 +7,4 @@ from visidata import vd, BaseSheet
|
|
7
7
|
|
8
8
|
vd.option('hello_world', '¡Hola mundo!', 'shown by the hello-world command')
|
9
9
|
|
10
|
-
BaseSheet.addCommand('
|
10
|
+
BaseSheet.addCommand('F2', 'hello-world', 'status(options.hello_world)', 'print greeting to status')
|
@@ -42,4 +42,4 @@ def addcol_expr(sheet):
|
|
42
42
|
|
43
43
|
|
44
44
|
Sheet.addCommand(None, 'addcol-expr', 'sheet.addcol_expr()', "create new column from Python expression, updating the column's calculated values live")
|
45
|
-
Sheet.addCommand(None, 'addcol-new', 'c=
|
45
|
+
Sheet.addCommand(None, 'addcol-new', 'c=addColumnAtCursor(SettableColumn(name="", width=options.default_width)); draw(sheet._scr); cursorVisibleColIndex=visibleCols.index(c); c.name=editCell(cursorVisibleColIndex, -1); c.width=None', 'append new column, updating the column name live')
|
visidata/expr.py
CHANGED
@@ -100,6 +100,7 @@ Sheet.addCommand('=', 'addcol-expr', 'addColumnAtCursor(ExprColumn(inputExpr("ne
|
|
100
100
|
Sheet.addCommand('g=', 'setcol-expr', 'cursorCol.setValuesFromExpr(someSelectedRows, inputExpr("set selected="), curcol=cursorCol)', 'set current column for selected rows to result of Python expression')
|
101
101
|
Sheet.addCommand('z=', 'setcell-expr', 'cursorCol.setValues([cursorRow], evalExpr(inputExpr("set expr="), row=cursorRow, curcol=cursorCol))', 'evaluate Python expression on current row and set current cell with result of Python expression')
|
102
102
|
Sheet.addCommand('gz=', 'setcol-iter', 'cursorCol.setValues(someSelectedRows, *list(itertools.islice(eval(input("set column= ", "expr", completer=CompleteExpr())), len(someSelectedRows))))', 'set current column for selected rows to the items in result of Python sequence expression')
|
103
|
+
Sheet.addCommand('', 'addcol-iter', 'iter_expr=inputExpr("new column iterator expr: "); it = eval(iter_expr, getGlobals()); c=SettableColumn(); addColumnAtCursor(c); c.setValues(rows, *it)', 'add column with values from a Python sequence expression, repeating it if needed to fill')
|
103
104
|
|
104
105
|
Sheet.addCommand(None, 'show-expr', 'status(evalExpr(inputExpr("show expr="), row=cursorRow, curcol=cursorCol))', 'evaluate Python expression on current row and show result on status line')
|
105
106
|
|
visidata/extensible.py
CHANGED
@@ -50,6 +50,7 @@ class Extensible:
|
|
50
50
|
from visidata import vd
|
51
51
|
func.importingModule = vd.importingModule
|
52
52
|
setattr(cls, func.__name__, func)
|
53
|
+
func._extensible_api = True
|
53
54
|
return func
|
54
55
|
|
55
56
|
@classmethod
|
@@ -97,6 +98,7 @@ class Extensible:
|
|
97
98
|
oldfunc = getattr(cls, name, None)
|
98
99
|
if oldfunc:
|
99
100
|
func = wraps(oldfunc)(func)
|
101
|
+
func._extensible_api = True
|
100
102
|
setattr(cls, name, func)
|
101
103
|
return func
|
102
104
|
|
@@ -107,6 +109,7 @@ class Extensible:
|
|
107
109
|
def dofunc(self):
|
108
110
|
return func(self)
|
109
111
|
setattr(cls, func.__name__, dofunc)
|
112
|
+
func._extensible_api = True
|
110
113
|
return dofunc
|
111
114
|
|
112
115
|
@classmethod
|
@@ -121,6 +124,7 @@ class Extensible:
|
|
121
124
|
setattr(self, name, func(self))
|
122
125
|
return getattr(self, name)
|
123
126
|
setattr(cls, func.__name__, get_if_not)
|
127
|
+
func._extensible_api = True
|
124
128
|
return get_if_not
|
125
129
|
|
126
130
|
@classmethod
|
visidata/features/cmdpalette.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
import collections
|
2
|
+
import math
|
2
3
|
from functools import partial
|
3
|
-
from visidata import DrawablePane, BaseSheet, vd, VisiData, CompleteKey, clipdraw, HelpSheet, colors, AcceptInput, AttrDict, drawcache_property
|
4
|
+
from visidata import DrawablePane, BaseSheet, vd, VisiData, CompleteKey, clipdraw, HelpSheet, colors, AcceptInput, AttrDict, drawcache_property, dispwidth
|
4
5
|
|
5
6
|
|
6
7
|
vd.theme_option('color_cmdpalette', 'black on 72', 'base color of command palette')
|
@@ -58,14 +59,21 @@ def inputPalette(sheet, prompt, items,
|
|
58
59
|
formatter=lambda m, item, trigger_key: f'{trigger_key} {item}',
|
59
60
|
multiple=False,
|
60
61
|
**kwargs):
|
61
|
-
if
|
62
|
+
if not vd.wantsHelp('cmdpalette'):
|
62
63
|
return vd.input(prompt,
|
63
64
|
completer=CompleteKey(sorted(item[value_key] for item in items)),
|
64
65
|
**kwargs)
|
65
66
|
|
66
67
|
bindings = dict()
|
67
68
|
|
69
|
+
#state variables for navigating display of matches
|
70
|
+
prev_value = None
|
68
71
|
tabitem = -1
|
72
|
+
offset = 0
|
73
|
+
def reset_display():
|
74
|
+
nonlocal tabitem, offset
|
75
|
+
tabitem = -1
|
76
|
+
offset = 0
|
69
77
|
|
70
78
|
def tab(n, nitems):
|
71
79
|
nonlocal tabitem
|
@@ -73,7 +81,11 @@ def inputPalette(sheet, prompt, items,
|
|
73
81
|
tabitem = (tabitem + n) % nitems
|
74
82
|
|
75
83
|
def _draw_palette(value):
|
76
|
-
|
84
|
+
nonlocal prev_value
|
85
|
+
words = value.split()
|
86
|
+
if value != prev_value:
|
87
|
+
reset_display()
|
88
|
+
prev_value = value
|
77
89
|
|
78
90
|
if multiple and words:
|
79
91
|
if value.endswith(' '):
|
@@ -83,8 +95,8 @@ def inputPalette(sheet, prompt, items,
|
|
83
95
|
finished_words = words[:-1]
|
84
96
|
unfinished_words = [words[-1]]
|
85
97
|
else:
|
86
|
-
unfinished_words = words
|
87
98
|
finished_words = []
|
99
|
+
unfinished_words = words
|
88
100
|
|
89
101
|
unuseditems = [item for item in items if item[value_key] not in finished_words]
|
90
102
|
|
@@ -92,25 +104,50 @@ def inputPalette(sheet, prompt, items,
|
|
92
104
|
|
93
105
|
h = sheet.windowHeight
|
94
106
|
w = min(100, sheet.windowWidth)
|
95
|
-
nitems = min(h-
|
107
|
+
nitems = min(h-2, sheet.options.disp_cmdpal_max)
|
108
|
+
if nitems <= 0:
|
109
|
+
return None
|
96
110
|
|
97
111
|
useditems = []
|
98
112
|
palrows = []
|
99
|
-
|
100
|
-
|
101
|
-
useditems
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
113
|
+
n_results = 0
|
114
|
+
def read_matches(offset):
|
115
|
+
nonlocal useditems, palrows, value, n_results
|
116
|
+
|
117
|
+
useditems = []
|
118
|
+
palrows = []
|
119
|
+
for m in matches[offset:offset+nitems]:
|
120
|
+
useditems.append(m.match)
|
121
|
+
palrows.append((m, m.match))
|
122
|
+
n_results += len(matches)
|
123
|
+
|
124
|
+
#List matches only, usually. But list the available choices when there's no input,
|
125
|
+
#or (if multiple is True) they've just pressed space after a word.
|
126
|
+
if not unfinished_words:
|
127
|
+
favitems = sorted([item for item in unuseditems if item not in useditems],
|
128
|
+
key=lambda item: -vd.usedInputs.get(item[value_key], 0))
|
129
|
+
for item in favitems[offset-len(palrows):offset+nitems-len(palrows)]:
|
130
|
+
palrows.append((None, item))
|
131
|
+
n_results += len(favitems)
|
132
|
+
read_matches(offset)
|
133
|
+
|
134
|
+
def change_page(dir=+1):
|
135
|
+
nonlocal offset, n_results, nitems
|
136
|
+
new_offset = offset + dir*nitems
|
137
|
+
# constrain offset to be a multiple of nitems
|
138
|
+
new_offset = min(new_offset, ((n_results-1) // nitems)*nitems)
|
139
|
+
new_offset = max(new_offset, 0)
|
140
|
+
if new_offset == offset: return None
|
141
|
+
offset = new_offset
|
109
142
|
|
110
143
|
navailitems = min(len(palrows), nitems)
|
111
144
|
|
112
145
|
bindings['^I'] = lambda *args: tab(1, navailitems) or args
|
113
146
|
bindings['KEY_BTAB'] = lambda *args: tab(-1, navailitems) or args
|
147
|
+
bindings['KEY_PPAGE'] = lambda *args: (change_page(-1) and read_matches(offset)) or args
|
148
|
+
bindings['KEY_NPAGE'] = lambda *args: (change_page(+1) and read_matches(offset)) or args
|
149
|
+
for numkey in '1234567890':
|
150
|
+
bindings.pop(numkey, None)
|
114
151
|
|
115
152
|
for i in range(nitems-len(palrows)):
|
116
153
|
palrows.append((None, None))
|
@@ -129,14 +166,13 @@ def inputPalette(sheet, prompt, items,
|
|
129
166
|
|
130
167
|
if tabitem < 0 and palrows:
|
131
168
|
_ , topitem = palrows[0]
|
132
|
-
if
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
169
|
+
if topitem:
|
170
|
+
if multiple:
|
171
|
+
bindings['^J'] = partial(accept_input_if_subset, value=topitem[value_key])
|
172
|
+
bindings[' '] = partial(add_to_input, value=topitem[value_key])
|
173
|
+
else:
|
174
|
+
bindings['^J'] = partial(accept_input, value=topitem[value_key])
|
138
175
|
elif item and i == tabitem:
|
139
|
-
if not item: return
|
140
176
|
if multiple:
|
141
177
|
bindings['^J'] = partial(accept_input_if_subset, value=item[value_key])
|
142
178
|
bindings[' '] = partial(add_to_input, value=item[value_key])
|
@@ -146,7 +182,10 @@ def inputPalette(sheet, prompt, items,
|
|
146
182
|
|
147
183
|
match_summary = formatter(m, item, trigger_key) if item else ' '
|
148
184
|
|
149
|
-
clipdraw(sheet._scr, h-nitems-
|
185
|
+
clipdraw(sheet._scr, h-nitems-2+i, 0, match_summary, attr, w=w)
|
186
|
+
attr = colors.color_cmdpalette
|
187
|
+
instr = 'Press [:keystrokes]PgUp/PgDn[/] to scroll items, [:keystrokes]Tab/Shift+Tab/Enter[/] to choose, [:keystrokes]Esc[/] to cancel.'
|
188
|
+
clipdraw(sheet._scr, h-2, 0, instr, attr, w=w)
|
150
189
|
|
151
190
|
return None
|
152
191
|
|
@@ -180,7 +219,7 @@ def inputLongname(sheet):
|
|
180
219
|
formatted_name = f'[:bold][:onclick {row.longname}]{formatted_longname}[/][/]'
|
181
220
|
if vd.options.debug and match:
|
182
221
|
keystrokes = f'[{match.score}]'
|
183
|
-
r = f' [:keystrokes]{keystrokes.rjust(
|
222
|
+
r = f' [:keystrokes]{keystrokes.rjust(dispwidth(prompt)-5)}[/] '
|
184
223
|
if trigger_key:
|
185
224
|
r += f'[:keystrokes]{trigger_key}[/]'
|
186
225
|
else:
|
@@ -213,4 +252,4 @@ def exec_longname(sheet, longname):
|
|
213
252
|
|
214
253
|
|
215
254
|
vd.addCommand('Space', 'exec-longname', 'exec_longname(inputLongname())', 'execute command by its longname')
|
216
|
-
vd.addCommand('zSpace', 'exec-longname-simple', 'exec_longname(inputLongnameSimple())', 'execute command by its longname')
|
255
|
+
vd.addCommand('zSpace', 'exec-longname-simple', 'exec_longname(inputLongnameSimple())', 'execute command by its longname (without command palette)')
|
visidata/features/describe.py
CHANGED
@@ -2,7 +2,7 @@ from copy import copy
|
|
2
2
|
from statistics import mode, median, mean, stdev
|
3
3
|
|
4
4
|
from visidata import vd, Column, ColumnAttr, vlen, RowColorizer, asyncthread, Progress, wrapply
|
5
|
-
from visidata import BaseSheet, TableSheet, ColumnsSheet,
|
5
|
+
from visidata import BaseSheet, TableSheet, ColumnsSheet, IndexSheet
|
6
6
|
|
7
7
|
|
8
8
|
vd.option('describe_aggrs', 'mean stdev', 'numeric aggregators to calculate on Describe sheet', help=vd.help_aggregators)
|
@@ -114,7 +114,7 @@ class DescribeSheet(ColumnsSheet):
|
|
114
114
|
|
115
115
|
TableSheet.addCommand('I', 'describe-sheet', 'vd.push(DescribeSheet(sheet.name+"_describe", source=[sheet]))', 'open Describe Sheet with descriptive statistics for all visible columns')
|
116
116
|
BaseSheet.addCommand('gI', 'describe-all', 'vd.push(DescribeSheet("describe_all", source=vd.stackedSheets))', 'open Describe Sheet with description statistics for all visible columns from all sheets')
|
117
|
-
|
117
|
+
IndexSheet.addCommand('gI', 'describe-selected', 'vd.push(DescribeSheet("describe_all", source=selectedRows))', 'open Describe Sheet with all visible columns from selected sheets')
|
118
118
|
|
119
119
|
DescribeSheet.addCommand('zs', 'select-cell', 'cursorRow.sheet.select(cursorValue)', 'select rows on source sheet which are being described in current cell')
|
120
120
|
DescribeSheet.addCommand('zu', 'unselect-cell', 'cursorRow.sheet.unselect(cursorValue)', 'unselect rows on source sheet which are being described in current cell')
|
visidata/features/expand_cols.py
CHANGED
@@ -121,8 +121,10 @@ class ExpandedColumn(Column):
|
|
121
121
|
def calcValue(self, row):
|
122
122
|
return getitemdef(self.origCol.getValue(row), self.expr)
|
123
123
|
|
124
|
-
def setValue(self, row, value):
|
124
|
+
def setValue(self, row, value, setModified=True):
|
125
125
|
self.origCol.getValue(row)[self.expr] = value
|
126
|
+
if setModified:
|
127
|
+
self.origCol.sheet.setModified()
|
126
128
|
|
127
129
|
|
128
130
|
@Sheet.api
|
@@ -181,10 +183,10 @@ Sheet.addCommand('g(', 'expand-cols', 'expand_cols_deep(visibleCols, depth=1)',
|
|
181
183
|
Sheet.addCommand('z(', 'expand-col-depth', 'expand_cols_deep([cursorCol], depth=int(input("expand depth=", value=0)))', 'expand current column of containers to given depth (0=fully)')
|
182
184
|
Sheet.addCommand('gz(', 'expand-cols-depth', 'expand_cols_deep(visibleCols, depth=int(input("expand depth=", value=0)))', 'expand all visible columns of containers to given depth (0=fully)')
|
183
185
|
|
184
|
-
Sheet.addCommand(')', 'contract-col', 'contract_cols([cursorCol])', 'remove current column and siblings from sheet columns and unhide parent')
|
185
|
-
Sheet.addCommand('g)', 'contract-cols', 'contract_cols(visibleCols)', 'remove all child columns and unhide toplevel parents')
|
186
|
-
Sheet.addCommand('z)', 'contract-col-depth', 'contract_cols([cursorCol], depth=int(input("contract depth=", value=0)))', 'remove current column and siblings from sheet columns and unhide parent')
|
187
|
-
Sheet.addCommand('gz)', 'contract-cols-depth', 'contract_cols(visibleCols, depth=int(input("contract depth=", value=0)))', 'remove all child columns and unhide toplevel parents')
|
186
|
+
Sheet.addCommand(')', 'contract-col', 'contract_cols([cursorCol])', 'remove current column and siblings from sheet columns and unhide parent (1 level)')
|
187
|
+
Sheet.addCommand('g)', 'contract-cols', 'contract_cols(visibleCols)', 'remove all child columns and unhide toplevel parents (1 level)')
|
188
|
+
Sheet.addCommand('z)', 'contract-col-depth', 'contract_cols([cursorCol], depth=int(input("contract depth=", value=0)))', 'remove current column and siblings from sheet columns and unhide parent, prompting for depth')
|
189
|
+
Sheet.addCommand('gz)', 'contract-cols-depth', 'contract_cols(visibleCols, depth=int(input("contract depth=", value=0)))', 'remove all child columns and unhide toplevel parents, prompting for depth')
|
188
190
|
|
189
191
|
ColumnsSheet.addCommand(')', 'contract-source-cols', 'source[0].addColumn(contract_source_cols(someSelectedRows), index=cursorRowIndex)', 'contract selected columns into column group') #1702
|
190
192
|
|
visidata/features/freeze.py
CHANGED
@@ -60,10 +60,22 @@ class StaticSheet(Sheet):
|
|
60
60
|
row.append(val)
|
61
61
|
|
62
62
|
|
63
|
+
@Sheet.api
|
64
|
+
def setcol_freeze(sheet, unfrozen): #2660 Contributed by @midichef
|
65
|
+
frozen = sheet.freeze_col(unfrozen)
|
66
|
+
frozen.name = unfrozen.name
|
67
|
+
unfrozen.hide()
|
68
|
+
vd.addUndoColNames([unfrozen])
|
69
|
+
unfrozen.name = frozen.name + '_unfrozen'
|
70
|
+
sheet.addColumnAtCursor(frozen)
|
71
|
+
vd.status(f'replaced {frozen.name} with frozen copy')
|
72
|
+
|
73
|
+
|
74
|
+
Sheet.addCommand("z'", 'setcol-freeze', 'setcol_freeze(cursorCol)', 'replace current column with a frozen copy, with all cells evaluated')
|
63
75
|
Sheet.addCommand("'", 'freeze-col', 'sheet.addColumnAtCursor(freeze_col(cursorCol))', 'add a frozen copy of current column with all cells evaluated')
|
64
76
|
Sheet.addCommand("g'", 'freeze-sheet', 'vd.push(StaticSheet(sheet)); status("pushed frozen copy of "+name)', 'open a frozen copy of current sheet with all visible columns evaluated')
|
65
|
-
Sheet.addCommand(
|
77
|
+
Sheet.addCommand(None, 'cache-col', 'cursorCol.resetCache()', 'add/reset cache for current column')
|
66
78
|
Sheet.addCommand("gz'", 'cache-cols', 'for c in visibleCols: c.resetCache()', 'add/reset cache for all visible columns')
|
67
79
|
|
68
|
-
vd.addMenuItem('Column', 'Freeze', 'freeze
|
80
|
+
vd.addMenuItem('Column', 'Freeze', 'setcol-freeze')
|
69
81
|
vd.addMenuItem('File', 'Freeze', 'freeze-sheet')
|