visidata 2.11.dev0__py3-none-any.whl → 3.0__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.
Files changed (253) hide show
  1. visidata/__init__.py +72 -91
  2. visidata/_input.py +263 -44
  3. visidata/_open.py +84 -29
  4. visidata/_types.py +22 -4
  5. visidata/_urlcache.py +17 -4
  6. visidata/aggregators.py +65 -25
  7. visidata/apps/__init__.py +0 -0
  8. visidata/apps/vdsql/__about__.py +8 -0
  9. visidata/apps/vdsql/__init__.py +5 -0
  10. visidata/apps/vdsql/__main__.py +27 -0
  11. visidata/apps/vdsql/_ibis.py +748 -0
  12. visidata/apps/vdsql/bigquery.py +61 -0
  13. visidata/apps/vdsql/clickhouse.py +53 -0
  14. visidata/apps/vdsql/setup.py +40 -0
  15. visidata/apps/vdsql/snowflake.py +67 -0
  16. visidata/apps/vgit/__init__.py +13 -0
  17. visidata/apps/vgit/__main__.py +3 -0
  18. visidata/apps/vgit/abort.py +23 -0
  19. visidata/apps/vgit/blame.py +76 -0
  20. visidata/apps/vgit/branch.py +153 -0
  21. visidata/apps/vgit/config.py +95 -0
  22. visidata/apps/vgit/diff.py +169 -0
  23. visidata/apps/vgit/gitsheet.py +161 -0
  24. visidata/apps/vgit/grep.py +37 -0
  25. visidata/apps/vgit/log.py +81 -0
  26. visidata/apps/vgit/main.py +55 -0
  27. visidata/apps/vgit/remote.py +57 -0
  28. visidata/apps/vgit/repos.py +71 -0
  29. visidata/apps/vgit/setup.py +37 -0
  30. visidata/apps/vgit/stash.py +69 -0
  31. visidata/apps/vgit/status.py +204 -0
  32. visidata/apps/vgit/statusbar.py +34 -0
  33. visidata/basesheet.py +59 -50
  34. visidata/canvas.py +251 -99
  35. visidata/choose.py +15 -11
  36. visidata/clean_names.py +29 -0
  37. visidata/clipboard.py +84 -18
  38. visidata/cliptext.py +220 -46
  39. visidata/cmdlog.py +89 -114
  40. visidata/color.py +142 -56
  41. visidata/column.py +134 -131
  42. visidata/ddw/input.ddw +74 -79
  43. visidata/ddw/regex.ddw +57 -0
  44. visidata/ddwplay.py +33 -14
  45. visidata/deprecated.py +77 -3
  46. visidata/desktop/visidata.desktop +7 -0
  47. visidata/editor.py +12 -6
  48. visidata/errors.py +5 -1
  49. visidata/experimental/__init__.py +0 -0
  50. visidata/experimental/diff_sheet.py +29 -0
  51. visidata/experimental/digit_autoedit.py +6 -0
  52. visidata/experimental/gdrive.py +89 -0
  53. visidata/experimental/google.py +37 -0
  54. visidata/experimental/gsheets.py +79 -0
  55. visidata/experimental/live_search.py +37 -0
  56. visidata/experimental/liveupdate.py +45 -0
  57. visidata/experimental/mark.py +133 -0
  58. visidata/experimental/noahs_tapestry/__init__.py +1 -0
  59. visidata/experimental/noahs_tapestry/tapestry.py +147 -0
  60. visidata/experimental/rownum.py +73 -0
  61. visidata/experimental/slide_cells.py +26 -0
  62. visidata/expr.py +8 -4
  63. visidata/extensible.py +32 -6
  64. visidata/features/__init__.py +0 -0
  65. visidata/features/addcol_audiometadata.py +42 -0
  66. visidata/features/addcol_histogram.py +34 -0
  67. visidata/features/canvas_save_svg.py +69 -0
  68. visidata/features/change_precision.py +46 -0
  69. visidata/features/cmdpalette.py +163 -0
  70. visidata/features/colorbrewer.py +363 -0
  71. visidata/{colorsheet.py → features/colorsheet.py} +17 -16
  72. visidata/features/command_server.py +105 -0
  73. visidata/features/currency_to_usd.py +70 -0
  74. visidata/{customdate.py → features/customdate.py} +2 -0
  75. visidata/features/dedupe.py +132 -0
  76. visidata/{describe.py → features/describe.py} +17 -15
  77. visidata/features/errors_guide.py +26 -0
  78. visidata/features/expand_cols.py +202 -0
  79. visidata/{fill.py → features/fill.py} +4 -2
  80. visidata/{freeze.py → features/freeze.py} +11 -6
  81. visidata/features/graph_seaborn.py +79 -0
  82. visidata/features/helloworld.py +10 -0
  83. visidata/features/hint_types.py +17 -0
  84. visidata/{incr.py → features/incr.py} +5 -0
  85. visidata/{join.py → features/join.py} +107 -53
  86. visidata/features/known_cols.py +21 -0
  87. visidata/features/layout.py +62 -0
  88. visidata/{melt.py → features/melt.py} +33 -21
  89. visidata/features/normcol.py +118 -0
  90. visidata/features/open_config.py +7 -0
  91. visidata/features/open_syspaste.py +18 -0
  92. visidata/features/ping.py +157 -0
  93. visidata/features/procmgr.py +208 -0
  94. visidata/features/random_sample.py +6 -0
  95. visidata/{regex.py → features/regex.py} +47 -31
  96. visidata/features/reload_every.py +55 -0
  97. visidata/features/rename_col_cascade.py +30 -0
  98. visidata/features/scroll_context.py +60 -0
  99. visidata/features/select_equal_selected.py +11 -0
  100. visidata/features/setcol_fake.py +65 -0
  101. visidata/{slide.py → features/slide.py} +75 -21
  102. visidata/features/sparkline.py +48 -0
  103. visidata/features/status_source.py +20 -0
  104. visidata/{sysedit.py → features/sysedit.py} +2 -1
  105. visidata/features/sysopen_mailcap.py +46 -0
  106. visidata/features/term_extras.py +13 -0
  107. visidata/{transpose.py → features/transpose.py} +5 -4
  108. visidata/features/type_ipaddr.py +73 -0
  109. visidata/features/type_url.py +11 -0
  110. visidata/{unfurl.py → features/unfurl.py} +9 -9
  111. visidata/{window.py → features/window.py} +2 -2
  112. visidata/form.py +50 -21
  113. visidata/freqtbl.py +81 -33
  114. visidata/fuzzymatch.py +414 -0
  115. visidata/graph.py +105 -33
  116. visidata/guide.py +180 -0
  117. visidata/help.py +75 -44
  118. visidata/hint.py +39 -0
  119. visidata/indexsheet.py +109 -0
  120. visidata/input_history.py +55 -0
  121. visidata/interface.py +58 -0
  122. visidata/keys.py +17 -16
  123. visidata/loaders/__init__.py +9 -0
  124. visidata/loaders/_pandas.py +61 -21
  125. visidata/loaders/api_airtable.py +70 -0
  126. visidata/loaders/api_bitio.py +102 -0
  127. visidata/loaders/api_matrix.py +148 -0
  128. visidata/loaders/api_reddit.py +306 -0
  129. visidata/loaders/api_zulip.py +249 -0
  130. visidata/loaders/archive.py +41 -7
  131. visidata/loaders/arrow.py +7 -7
  132. visidata/loaders/conll.py +49 -0
  133. visidata/loaders/csv.py +25 -7
  134. visidata/loaders/eml.py +3 -4
  135. visidata/loaders/f5log.py +1204 -0
  136. visidata/loaders/fec.py +325 -0
  137. visidata/loaders/fixed_width.py +3 -5
  138. visidata/loaders/frictionless.py +3 -3
  139. visidata/loaders/geojson.py +8 -5
  140. visidata/loaders/google.py +48 -0
  141. visidata/loaders/graphviz.py +4 -4
  142. visidata/loaders/hdf5.py +4 -4
  143. visidata/loaders/html.py +48 -10
  144. visidata/loaders/http.py +84 -30
  145. visidata/loaders/imap.py +20 -10
  146. visidata/loaders/jrnl.py +52 -0
  147. visidata/loaders/json.py +83 -29
  148. visidata/loaders/jsonla.py +74 -0
  149. visidata/loaders/lsv.py +15 -11
  150. visidata/loaders/mailbox.py +40 -0
  151. visidata/loaders/markdown.py +1 -3
  152. visidata/loaders/mbtiles.py +4 -5
  153. visidata/loaders/mysql.py +11 -13
  154. visidata/loaders/npy.py +7 -7
  155. visidata/loaders/odf.py +4 -1
  156. visidata/loaders/orgmode.py +428 -0
  157. visidata/loaders/pandas_freqtbl.py +14 -20
  158. visidata/loaders/parquet.py +62 -6
  159. visidata/loaders/pcap.py +3 -3
  160. visidata/loaders/pdf.py +4 -3
  161. visidata/loaders/png.py +19 -13
  162. visidata/loaders/postgres.py +9 -8
  163. visidata/loaders/rec.py +7 -3
  164. visidata/loaders/s3.py +342 -0
  165. visidata/loaders/sas.py +5 -5
  166. visidata/loaders/scrape.py +186 -0
  167. visidata/loaders/shp.py +6 -5
  168. visidata/loaders/spss.py +5 -6
  169. visidata/loaders/sqlite.py +68 -28
  170. visidata/loaders/texttables.py +1 -1
  171. visidata/loaders/toml.py +60 -0
  172. visidata/loaders/tsv.py +61 -19
  173. visidata/loaders/ttf.py +19 -7
  174. visidata/loaders/unzip_http.py +6 -5
  175. visidata/loaders/usv.py +1 -1
  176. visidata/loaders/vcf.py +16 -16
  177. visidata/loaders/vds.py +10 -7
  178. visidata/loaders/vdx.py +30 -5
  179. visidata/loaders/xlsb.py +8 -1
  180. visidata/loaders/xlsx.py +145 -25
  181. visidata/loaders/xml.py +6 -3
  182. visidata/loaders/xword.py +4 -4
  183. visidata/loaders/yaml.py +15 -5
  184. visidata/macos.py +1 -1
  185. visidata/macros.py +130 -41
  186. visidata/main.py +119 -94
  187. visidata/mainloop.py +101 -154
  188. visidata/man/parse_options.py +2 -2
  189. visidata/man/vd.1 +302 -147
  190. visidata/man/vd.txt +291 -151
  191. visidata/memory.py +3 -3
  192. visidata/menu.py +104 -423
  193. visidata/metasheets.py +59 -141
  194. visidata/modify.py +79 -23
  195. visidata/motd.py +3 -3
  196. visidata/mouse.py +137 -0
  197. visidata/movement.py +43 -35
  198. visidata/optionssheet.py +99 -0
  199. visidata/path.py +131 -43
  200. visidata/pivot.py +74 -47
  201. visidata/plugins.py +65 -192
  202. visidata/pyobj.py +50 -201
  203. visidata/rename_col.py +20 -0
  204. visidata/save.py +42 -20
  205. visidata/search.py +54 -10
  206. visidata/selection.py +84 -5
  207. visidata/settings.py +162 -24
  208. visidata/sheets.py +229 -257
  209. visidata/shell.py +51 -21
  210. visidata/sidebar.py +162 -0
  211. visidata/sort.py +11 -4
  212. visidata/statusbar.py +113 -104
  213. visidata/stored_list.py +43 -0
  214. visidata/stored_prop.py +38 -0
  215. visidata/tests/conftest.py +3 -3
  216. visidata/tests/test_cliptext.py +39 -0
  217. visidata/tests/test_commands.py +62 -7
  218. visidata/tests/test_edittext.py +2 -2
  219. visidata/tests/test_features.py +17 -0
  220. visidata/tests/test_menu.py +14 -0
  221. visidata/tests/test_path.py +13 -4
  222. visidata/text_source.py +53 -0
  223. visidata/textsheet.py +10 -3
  224. visidata/theme.py +44 -0
  225. visidata/themes/__init__.py +0 -0
  226. visidata/themes/ascii8.py +84 -0
  227. visidata/themes/asciimono.py +84 -0
  228. visidata/themes/light.py +17 -0
  229. visidata/threads.py +87 -39
  230. visidata/tuiwin.py +22 -0
  231. visidata/type_currency.py +22 -3
  232. visidata/type_date.py +31 -9
  233. visidata/type_floatsi.py +5 -1
  234. visidata/undo.py +18 -6
  235. visidata/utils.py +106 -23
  236. visidata/vdobj.py +28 -17
  237. visidata/windows.py +10 -0
  238. visidata/wrappers.py +9 -3
  239. visidata-3.0.data/data/share/applications/visidata.desktop +7 -0
  240. {visidata-2.11.dev0.data → visidata-3.0.data}/data/share/man/man1/vd.1 +302 -147
  241. {visidata-2.11.dev0.data → visidata-3.0.data}/data/share/man/man1/visidata.1 +302 -147
  242. visidata-3.0.data/scripts/vd2to3.vdx +9 -0
  243. {visidata-2.11.dev0.dist-info → visidata-3.0.dist-info}/METADATA +13 -11
  244. visidata-3.0.dist-info/RECORD +257 -0
  245. {visidata-2.11.dev0.dist-info → visidata-3.0.dist-info}/WHEEL +1 -1
  246. {visidata-2.11.dev0.dist-info → visidata-3.0.dist-info}/entry_points.txt +0 -1
  247. visidata/layout.py +0 -44
  248. visidata/misc.py +0 -5
  249. visidata-2.11.dev0.dist-info/RECORD +0 -142
  250. /visidata/{repeat.py → features/repeat.py} +0 -0
  251. {visidata-2.11.dev0.data → visidata-3.0.data}/scripts/vd +0 -0
  252. {visidata-2.11.dev0.dist-info → visidata-3.0.dist-info}/LICENSE.gpl3 +0 -0
  253. {visidata-2.11.dev0.dist-info → visidata-3.0.dist-info}/top_level.txt +0 -0
visidata/column.py CHANGED
@@ -7,10 +7,11 @@ import re
7
7
  import time
8
8
  import json
9
9
 
10
- from visidata import options, anytype, stacktrace, vd
10
+ from visidata import options, anytype, stacktrace, vd, drawcache
11
11
  from visidata import asyncthread, dispwidth, clipstr, iterchars
12
12
  from visidata import wrapply, TypedWrapper, TypedExceptionWrapper
13
- from visidata import Extensible, AttrDict, undoAttrFunc
13
+ from visidata import Extensible, AttrDict, undoAttrFunc, ExplodingMock, MissingAttrFormatter
14
+ from visidata import getitem, setitem, getitemdef, getitemdeep, setitemdeep, getattrdeep, setattrdeep, iterchunks
14
15
 
15
16
  class InProgress(Exception):
16
17
  @property
@@ -19,31 +20,16 @@ class InProgress(Exception):
19
20
 
20
21
  INPROGRESS = TypedExceptionWrapper(None, exception=InProgress()) # sentinel
21
22
 
22
- vd.option('col_cache_size', 0, 'max number of cache entries in each cached column')
23
- vd.option('clean_names', False, 'clean column/sheet names to be valid Python identifiers', replay=True)
24
-
25
- __all__ = [
26
- 'clean_to_id',
27
- 'Column',
28
- 'setitem',
29
- 'getattrdeep',
30
- 'setattrdeep',
31
- 'getitemdef',
32
- 'ColumnAttr', 'AttrColumn',
33
- 'ColumnItem', 'ItemColumn',
34
- 'SettableColumn',
35
- 'SubColumnFunc',
36
- 'SubColumnItem',
37
- 'SubColumnAttr',
38
- 'ColumnExpr', 'ExprColumn',
39
- 'DisplayWrapper',
40
- ]
23
+ vd.option('col_cache_size', 0, 'max number of cache entries in each cached column', max_help=-1)
24
+ vd.option('disp_formatter', 'generic', 'formatter to create the text in each cell (also used by text savers)', replay=True, max_help=0)
25
+ vd.option('disp_displayer', 'generic', 'displayer to render the text in each cell', replay=False, max_help=0)
41
26
 
42
27
 
43
28
  class DisplayWrapper:
44
- def __init__(self, value=None, *, display=None, note=None, notecolor=None, error=None):
29
+ def __init__(self, value=None, *, typedval=None, text=None, note=None, notecolor=None, error=None):
45
30
  self.value = value # actual value (any type)
46
- self.display = display # displayed string
31
+ self.typedval = typedval # consistently typed value (or None)
32
+ self.text = text # displayed string
47
33
  self.note = note # single unicode character displayed in cell far right
48
34
  self.notecolor = notecolor # configurable color name (like 'color_warning')
49
35
  self.error = error # list of strings for stacktrace
@@ -54,11 +40,6 @@ class DisplayWrapper:
54
40
  def __eq__(self, other):
55
41
  return self.value == other
56
42
 
57
-
58
- def clean_to_id(s): # [Nas Banov] https://stackoverflow.com/a/3305731
59
- return re.sub(r'\W|^(?=\d)', '_', str(s)).strip('_')
60
-
61
-
62
43
  def _default_colnames():
63
44
  'A B C .. Z AA AB .. ZZ AAA .. to infinity'
64
45
  i=0
@@ -88,14 +69,14 @@ class Column(Extensible):
88
69
  - *kwargs*: other attributes to be set on this column.
89
70
  '''
90
71
  def __init__(self, name=None, *, type=anytype, cache=False, **kwargs):
91
- self.sheet = None # owning Sheet, set in .recalc() via Sheet.addColumn
72
+ self.sheet = ExplodingMock('use addColumn() on all columns') # owning Sheet, set in .recalc() via Sheet.addColumn
92
73
  if name is None:
93
74
  name = next(default_colnames)
94
75
  self.name = str(name) # display visible name
95
76
  self.fmtstr = '' # by default, use str()
96
77
  self._type = type # anytype/str/int/float/date/func
97
78
  self.getter = lambda col, row: row
98
- self.setter = lambda col, row, value: vd.fail(col.name+' column cannot be changed')
79
+ self.setter = None
99
80
  self._width = None # == 0 if hidden, None if auto-compute next time
100
81
  self.hoffset = 0 # starting horizontal (char) offset of displayed column value
101
82
  self.voffset = 0 # starting vertical (line) offset of displayed column value
@@ -103,7 +84,9 @@ class Column(Extensible):
103
84
  self.keycol = 0 # keycol index (or 0 if not key column)
104
85
  self.expr = None # Column-type-dependent parameter
105
86
  self.formatter = ''
87
+ self.displayer = ''
106
88
  self.defer = False
89
+ self.max_help = 10 # auto-hide above this disp_help level
107
90
 
108
91
  self.setCache(cache)
109
92
  for k, v in kwargs.items():
@@ -118,13 +101,17 @@ class Column(Extensible):
118
101
  ret._cachedValues = collections.OrderedDict() # an unrelated cache for copied columns
119
102
  return ret
120
103
 
104
+ def __str__(self):
105
+ return f'{type(self).__name__}:{self.name}'
106
+
107
+ def __repr__(self):
108
+ return f'<{type(self).__name__}: {self.name}>'
109
+
121
110
  def __deepcopy__(self, memo):
122
111
  return self.__copy__() # no separate deepcopy
123
112
 
124
113
  def __getstate__(self):
125
- d = {k:getattr(self, k) for k in 'name width height expr keycol formatter fmtstr voffset hoffset aggstr'.split() if hasattr(self, k)}
126
- d['type'] = self.type.__name__
127
- return d
114
+ return {k:getattr(self, k) for k in 'name typestr width height expr keycol formatter fmtstr voffset hoffset aggstr'.split() if hasattr(self, k)}
128
115
 
129
116
  def __setstate__(self, d):
130
117
  for attr, v in d.items():
@@ -145,6 +132,9 @@ class Column(Extensible):
145
132
 
146
133
  @name.setter
147
134
  def name(self, name):
135
+ self.setName(name)
136
+
137
+ def setName(self, name):
148
138
  if name is None:
149
139
  name = ''
150
140
  if isinstance(name, str):
@@ -194,6 +184,20 @@ class Column(Extensible):
194
184
  vd.addUndo(setattr, self, '_width', self.width)
195
185
  self._width = w
196
186
 
187
+ @property
188
+ def formatted_help(self):
189
+ return MissingAttrFormatter().format(self.help, sheet=self.sheet, col=self, vd=vd)
190
+
191
+ @property
192
+ def help_formatters(self):
193
+ formatters = [k[10:] for k in dir(self) if k.startswith('formatter_')]
194
+ return ' '.join(formatters)
195
+
196
+ @property
197
+ def help_displayers(self):
198
+ displayers = [k[10:] for k in dir(self) if k.startswith('displayer_')]
199
+ return ' '.join(displayers)
200
+
197
201
  @property
198
202
  def _formatdict(col):
199
203
  if '=' in col.fmtstr:
@@ -229,6 +233,7 @@ class Column(Extensible):
229
233
  def formatter_python(self, fmtstr):
230
234
  return lambda v,*args,**kwargs: str(v)
231
235
 
236
+ @drawcache
232
237
  def make_formatter(self):
233
238
  'Return function for format(v) from the current formatter and fmtstr'
234
239
  _formatMaker = getattr(self, 'formatter_'+(self.formatter or self.sheet.options.disp_formatter))
@@ -250,7 +255,34 @@ class Column(Extensible):
250
255
  if isinstance(typedval, bytes):
251
256
  typedval = typedval.decode(options.encoding, options.encoding_errors)
252
257
 
253
- return vd.getType(self.type).formatter(self.fmtstr, typedval)
258
+ gt = vd.getType(self._type)
259
+ return gt.formatter(self._fmtstr or gt.fmtstr, typedval)
260
+
261
+ def displayer_generic(self, dw:DisplayWrapper, width=None):
262
+ '''Fit *dw.text* into *width* charcells.
263
+ Generate list of (attr:str, text:str) suitable for clipdraw_chunks.
264
+
265
+ The 'generic' displayer does not do any formatting.
266
+ '''
267
+ if width is not None and width > 1 and vd.isNumeric(self):
268
+ yield ('', dw.text.rjust(width-2))
269
+ else:
270
+ yield ('', dw.text)
271
+
272
+ def displayer_full(self, dw:DisplayWrapper, width=None):
273
+ '''Fit *dw.text* into *width* charcells.
274
+ Generate list of (attr:str, text:str) suitable for clipdraw_chunks.
275
+
276
+ The 'full' displayer allows formatting like [:color].
277
+ '''
278
+ if width is not None and width > 1 and vd.isNumeric(self):
279
+ yield from iterchunks(text.rjust(width-2))
280
+ else:
281
+ yield from iterchunks(dw.text)
282
+
283
+ def display(self, *args, **kwargs):
284
+ f = getattr(self, 'displayer_'+(self.displayer or self.sheet.options.disp_displayer), self.displayer_generic)
285
+ return f(*args, **kwargs)
254
286
 
255
287
  def hide(self, hide=True):
256
288
  if hide:
@@ -284,7 +316,7 @@ class Column(Extensible):
284
316
 
285
317
  @asyncthread
286
318
  def _calcIntoCacheAsync(self, row):
287
- # causes isues when moved into _calcIntoCache gen case
319
+ # causes issues when moved into _calcIntoCache gen case
288
320
  self._cachedValues[self.sheet.rowid(row)] = INPROGRESS
289
321
  self._calcIntoCache(row)
290
322
 
@@ -335,34 +367,35 @@ class Column(Extensible):
335
367
  else:
336
368
  dispval = options.disp_error_val
337
369
  return DisplayWrapper(cellval.val, error=exc.stacktrace,
338
- display=dispval,
370
+ text=dispval,
339
371
  note=options.note_getter_exc,
340
372
  notecolor='color_error')
341
373
  elif typedval.val is None: # early out for strict None
342
- return DisplayWrapper(None, display='', # force empty display for None
374
+ return DisplayWrapper(None, text='', # force empty display for None
343
375
  note=options.disp_note_none,
344
376
  notecolor='color_note_type')
345
377
  elif isinstance(typedval, TypedExceptionWrapper): # calc succeeded, type failed
346
- return DisplayWrapper(typedval.val, display=str(cellval),
378
+ return DisplayWrapper(typedval.val, text=str(cellval),
347
379
  error=typedval.stacktrace,
348
380
  note=options.note_type_exc,
349
381
  notecolor='color_warning')
350
382
  else:
351
- return DisplayWrapper(typedval.val, display=str(typedval.val),
383
+ return DisplayWrapper(typedval.val, text=str(typedval.val),
352
384
  error='unknown',
353
385
  note=options.note_type_exc,
354
386
  notecolor='color_warning')
355
387
 
356
388
  elif isinstance(typedval, threading.Thread):
357
389
  return DisplayWrapper(None,
358
- display=options.disp_pending,
390
+ text=options.disp_pending,
359
391
  note=options.note_pending,
360
392
  notecolor='color_note_pending')
361
393
 
362
394
  dw = DisplayWrapper(cellval)
395
+ dw.typedval = typedval
363
396
 
364
397
  try:
365
- dw.display = self.format(typedval, width=(self.width or 0)*2) or ''
398
+ dw.text = self.format(typedval, width=(self.width or 0)*2) or ''
366
399
 
367
400
  # annotate cells with raw value type in anytype columns, except for strings
368
401
  if self.type is anytype and type(cellval) is not str:
@@ -375,43 +408,41 @@ class Column(Extensible):
375
408
  e.stacktrace = stacktrace()
376
409
  dw.error = e.stacktrace
377
410
  try:
378
- dw.display = str(cellval)
411
+ dw.text = str(cellval)
379
412
  except Exception as e:
380
- dw.display = str(e)
413
+ dw.text = str(e)
381
414
  dw.note = options.note_format_exc
382
415
  dw.notecolor = 'color_warning'
383
416
 
417
+ # dw.display = self.display(dw) # set during draw() when colwidth is known
384
418
  return dw
385
419
 
386
420
  def getDisplayValue(self, row):
387
421
  'Return string displayed in this column for given *row*.'
388
- return self.getCell(row).display
422
+ return self.getCell(row).text
389
423
 
390
424
  def putValue(self, row, val):
391
425
  'Change value for *row* in this column to *val* immediately. Does not check the type. Overridable; by default calls ``.setter(row, val)``.'
392
- return self.setter(self, row, val)
426
+ if self.setter:
427
+ return self.setter(self, row, val)
393
428
 
394
- def setValue(self, row, val):
429
+ def setValue(self, row, val, setModified=True):
395
430
  'Change value for *row* in this column to *val*. Call ``putValue`` immediately if not a deferred column (added to deferred parent at load-time); otherwise cache until later ``putChanges``. Caller must add undo function.'
396
431
  if self.defer:
397
432
  self.cellChanged(row, val)
398
433
  else:
399
434
  self.putValue(row, val)
400
- self.sheet.setModified()
401
-
402
- def setValueSafe(self, row, value):
403
- 'setValue and ignore exceptions.'
404
- try:
405
- return self.setValue(row, value)
406
- except Exception as e:
407
- vd.exceptionCaught(e)
435
+ if setModified: #1800
436
+ self.sheet.setModified()
408
437
 
409
438
  @asyncthread
410
439
  def setValues(self, rows, *values):
411
440
  'Set values in this column for *rows* to *values*, recycling values as needed to fill *rows*.'
412
441
  vd.addUndoSetValues([self], rows)
442
+
413
443
  for r, v in zip(rows, itertools.cycle(values)):
414
- self.setValueSafe(r, v)
444
+ vd.callNoExceptions(self.setValue, r, v)
445
+
415
446
  self.recalc()
416
447
  return vd.status('set %d cells to %d values' % (len(rows), len(values)))
417
448
 
@@ -419,7 +450,7 @@ class Column(Extensible):
419
450
  'Set values on this column for *rows* to *values*, coerced to column type, recycling values as needed to fill *rows*. Abort on type exception.'
420
451
  vd.addUndoSetValues([self], rows)
421
452
  for r, v in zip(rows, itertools.cycle(self.type(val) for val in values)):
422
- self.setValueSafe(r, v)
453
+ vd.callNoExceptions(self.setValue, r, v)
423
454
 
424
455
  self.recalc()
425
456
 
@@ -430,92 +461,47 @@ class Column(Extensible):
430
461
  w = 0
431
462
  nlen = dispwidth(self.name)
432
463
  if len(rows) > 0:
433
- w = max(max(dispwidth(self.getDisplayValue(r), maxwidth=self.sheet.windowWidth) for r in rows), nlen)+2
434
- return max(w, nlen)
464
+ w_max = 0
465
+ for r in rows:
466
+ row_w = dispwidth(self.getDisplayValue(r), maxwidth=self.sheet.windowWidth)
467
+ if w_max < row_w:
468
+ w_max = row_w
469
+ if w_max >= self.sheet.windowWidth:
470
+ break #1747 early out to speed up wide columns
471
+ w = w_max
472
+ w = max(w, nlen)+2
473
+ w = min(w, self.sheet.windowWidth)
474
+ return w
435
475
 
436
476
 
437
- # ---- Column makers
438
477
 
439
- def setitem(r, i, v): # function needed for use in lambda
440
- r[i] = v
441
- return True
478
+ # ---- basic Columns
442
479
 
443
-
444
- def getattrdeep(obj, attr, *default, getter=getattr):
445
- try:
446
- 'Return dotted attr (like "a.b.c") from obj, or default if any of the components are missing.'
447
- if not isinstance(attr, str):
448
- return getter(obj, attr, *default)
449
-
450
- try: # if attribute exists, return toplevel value, even if dotted
451
- if attr in obj:
452
- return getter(obj, attr)
453
- except Exception as e:
454
- pass
455
-
456
- attrs = attr.split('.')
457
- for a in attrs[:-1]:
458
- obj = getter(obj, a)
459
-
460
- return getter(obj, attrs[-1])
461
- except Exception as e:
462
- if not default: raise
463
- return default[0]
464
-
465
-
466
- def setattrdeep(obj, attr, val, getter=getattr, setter=setattr):
467
- 'Set dotted attr (like "a.b.c") on obj to val.'
468
- if not isinstance(attr, str):
469
- return setter(obj, attr, val)
470
-
471
- try: # if attribute exists, overwrite toplevel value, even if dotted
472
- getter(obj, attr)
473
- return setter(obj, attr, val)
474
- except Exception as e:
475
- pass
476
-
477
- attrs = attr.split('.')
478
- for a in attrs[:-1]:
479
- try:
480
- obj = getter(obj, a)
481
- except Exception as e:
482
- obj = obj[a] = type(obj)() # assume homogeneous nesting
483
-
484
- setter(obj, attrs[-1], val)
485
-
486
-
487
- def getitemdeep(obj, k, *default):
488
- return getattrdeep(obj, k, *default, getter=getitem)
489
-
490
- def setitemdeep(obj, k, val):
491
- return setattrdeep(obj, k, val, getter=getitemdef, setter=setitem)
492
-
493
- def AttrColumn(name='', attr=None, **kwargs):
480
+ class AttrColumn(Column):
494
481
  'Column using getattr/setattr with *attr*.'
495
- return Column(name,
496
- expr=attr if attr is not None else name,
497
- getter=lambda col,row: getattrdeep(row, col.expr),
498
- setter=lambda col,row,val: setattrdeep(row, col.expr, val),
499
- **kwargs)
482
+ def __init__(self, name=None, expr=None, **kwargs):
483
+ super().__init__(name,
484
+ expr=expr if expr is not None else name,
485
+ getter=lambda col,row: getattrdeep(row, col.expr, None),
486
+ **kwargs)
500
487
 
501
- def getitem(o, k, default=None):
502
- return default if o is None else o[k]
488
+ def putValue(self, row, val):
489
+ super().putValue(row, val)
490
+ setattrdeep(row, self.expr, val)
503
491
 
504
- def getitemdef(o, k, default=None):
505
- try:
506
- return default if o is None else o[k]
507
- except Exception:
508
- return default
509
492
 
510
493
  class ItemColumn(Column):
511
- 'Column using getitem/setitem with *key*.'
494
+ 'Column using getitem/setitem with *expr*.'
512
495
  def __init__(self, name=None, expr=None, **kwargs):
513
496
  super().__init__(name,
514
497
  expr=expr if expr is not None else name,
515
498
  getter=lambda col,row: getitemdeep(row, col.expr, None),
516
- setter=lambda col,row,val: setitemdeep(row, col.expr, val),
517
499
  **kwargs)
518
500
 
501
+ def putValue(self, row, val):
502
+ super().putValue(row, val)
503
+ setitemdeep(row, self.expr, val)
504
+
519
505
 
520
506
  class SubColumnFunc(Column):
521
507
  'Column compositor; preprocess row with *subfunc*(row, *expr*) before passing to *origcol*.getValue and *origcol*.setValue.'
@@ -573,7 +559,7 @@ class ExprColumn(Column):
573
559
  a = self.getDisplayValue(row)
574
560
  b = self.format(self.type(val))
575
561
  if a != b:
576
- vd.warning('%s calced %s not %s' % (self.name, a, b))
562
+ vd.warning("Cannot change value of calculated column. Use `'` to freeze column.")
577
563
 
578
564
  @property
579
565
  def expr(self):
@@ -597,7 +583,24 @@ class SettableColumn(Column):
597
583
  SettableColumn.init('_store', dict, copy=True)
598
584
 
599
585
 
586
+ vd.addGlobals(
587
+ INPROGRESS=INPROGRESS,
588
+ Column=Column,
589
+ setitem=setitem,
590
+ getattrdeep=getattrdeep,
591
+ setattrdeep=setattrdeep,
592
+ getitemdef=getitemdef,
593
+ AttrColumn=AttrColumn,
594
+ ItemColumn=ItemColumn,
595
+ ExprColumn=ExprColumn,
596
+ SettableColumn=SettableColumn,
597
+ SubColumnFunc=SubColumnFunc,
598
+ SubColumnItem=SubColumnItem,
599
+ SubColumnAttr=SubColumnAttr,
600
+
600
601
  # synonyms
601
- ColumnItem = ItemColumn
602
- ColumnAttr = AttrColumn
603
- ColumnExpr = ExprColumn
602
+ ColumnItem=ItemColumn,
603
+ ColumnAttr=AttrColumn,
604
+ ColumnExpr=ExprColumn,
605
+ DisplayWrapper=DisplayWrapper
606
+ )