Repository: saulpw/visidata

Issue:
[dev 2.0+] disp_rstatus_fmt causes halting exception on GraphSheet
**Small description**
I modified the `disp_rstatus_fmt` to provide information I require when working with sheets, however when using GraphSheet vd crashes with the following exception. I currently have to use `^g` to see the same information as I would be able to see using this `disp_rstatus_fmt`.

`options.disp_rstatus_fmt = ' {sheet.cursorRowIndex}/{sheet.nRows}[{sheet.nSelectedRows}] {sheet.rowtype} '`

`AttributeError: 'GraphSheet' object has no attribute 'cursorRowIndex'`

**Expected result**
GraphSheet to load

FWIW, I was also attempting to make a % counter in `disp_rstatus_fmt` however I get some odd other exceptions (this isn't really an issue so I haven't opened a separate bug, however this was my original use-case).


Discussion:
To add, I also can't do something like this:
`options.disp_rstatus_fmt = ' {sheet.cursorRowIndex or ""}/{sheet.nRows}[{sheet.nSelectedRows}] {sheet.rowtype} '`

Exception
`AttributeError: 'JsonSheet' object has no attribute 'cursorRowIndex or ""'`

Resolving patch:
diff --git a/visidata/statusbar.py b/visidata/statusbar.py
--- a/visidata/statusbar.py
+++ b/visidata/statusbar.py
@@ -1,7 +1,7 @@
 import collections
 import curses
 
-from visidata import vd, VisiData, BaseSheet, Sheet, ColumnItem, Column, RowColorizer, options, colors, wrmap, clipdraw, ExpectedException, update_attr, theme
+from visidata import vd, VisiData, BaseSheet, Sheet, ColumnItem, Column, RowColorizer, options, colors, wrmap, clipdraw, ExpectedException, update_attr, theme, MissingAttrFormatter
 
 
 __all__ = ['StatusSheet', 'status', 'error', 'fail', 'warning', 'debug']
@@ -153,7 +153,7 @@ def drawLeftStatus(vd, scr, vs):
 @VisiData.api
 def rightStatus(vd, sheet):
     'Return right side of status bar.  Overrideable.'
-    return options.disp_rstatus_fmt.format(sheet=sheet, vd=vd)
+    return MissingAttrFormatter().format(sheet.options.disp_rstatus_fmt, sheet=sheet, vd=vd)
 
 
 @VisiData.api
diff --git a/visidata/utils.py b/visidata/utils.py
--- a/visidata/utils.py
+++ b/visidata/utils.py
@@ -1,8 +1,9 @@
 import operator
+import string
 
 'Various helper classes and functions.'
 
-__all__ = ['AlwaysDict', 'AttrDict', 'moveListItem', 'namedlist', 'classproperty']
+__all__ = ['AlwaysDict', 'AttrDict', 'moveListItem', 'namedlist', 'classproperty', 'MissingAttrFormatter']
 
 
 class AlwaysDict(dict):
@@ -98,3 +99,17 @@ def __setattr__(self, k, v):
                 super().__setattr__(k, v)
 
     return NamedListTemplate
+
+class MissingAttrFormatter(string.Formatter):
+    "formats {} fields with `''`, that would normally result in a raised KeyError or AttributeError; intended for user customisable format strings."
+    def get_field(self, field_name, *args, **kwargs):
+        try:
+            return super().get_field(field_name, *args, **kwargs)
+        except (KeyError, AttributeError):
+            return (None, field_name)
+
+    def format_field(self, value, format_spec):
+        # value is missing
+        if not value:
+            return ''
+        return super().format_field(value, format_spec)


Test patch: