Repository: saulpw/visidata

Issue:
[html saver] Saving typed columns as html (int/vlen/bool) causes exception
I tried to copy (yank) a couple of rows from the frequency sheet and it provided me the following error. I believe this is due to the html parser expecting strings? A similar error also occurs in other sheets when using unexpected py types (e.g. bool).

FrequencySheet error
```
Traceback (most recent call last):
  File "/Documents/pyv/py3/lib/python3.7/site-packages/visidata/threads.py", line 201, in _toplevelTryFunc
    t.status = func(*args, **kwargs)
  File "/Documents/pyv/py3/lib/python3.7/site-packages/visidata/loaders/html.py", line 124, in save_html
    fp.write(html.escape(val))
  File "/usr/local/Cellar/python/3.7.6_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/html/__init__.py", line 19, in escape
    s = s.replace("&", "&amp;") # Must be done first!
AttributeError: 'vlen' object has no attribute 'replace'
```

Sheet with a bool column error:
```
Traceback (most recent call last):
  File "/Documents/pyv/py3/lib/python3.7/site-packages/visidata/threads.py", line 201, in _toplevelTryFunc
    t.status = func(*args, **kwargs)
  File "/Documents/pyv/py3/lib/python3.7/site-packages/visidata/loaders/html.py", line 124, in save_html
    fp.write(html.escape(val))
  File "/usr/local/Cellar/python/3.7.6_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/html/__init__.py", line 19, in escape
    s = s.replace("&", "&amp;") # Must be done first!
AttributeError: 'bool' object has no attribute 'replace'
```


Discussion:
Hey @geekscrapy!

Thanks for the bug report. I was sadly unable to reproduce this with the information you gave me. 

I took the `sample_data/sunshinelist.html`. I opened up one of the tables, I performed a frequency table.

I yanked, and it successfully yanked. No error.

Can you please provide me with:
* sample data
* the steps you took on this data
* the version of VisiData you are using
Hey @anjakefala 

Details of my environment:
* Python 3.7.6 (running in venv)
* saul.pw/VisiData v2.-4dev : commit 9e89a6071cf5cc3910988658830583a4c85028bf

I can replicate given the following steps:

Save the following command log to: html_exp.vd
```
sheet	col	row	longname	input	keystrokes	comment
	override	filetype	set-option	csv		
			open-file	-	o	
-	int		type-int		#	set type of current column to int
```

`echo -e "int,bool\n123,True" | vd -f csv --play html_exp.vd`

Now `Y` the first row and save as html. I get the following exception:
```
Traceback (most recent call last):
  File "/Documents/pyv/py3/lib/python3.7/site-packages/visidata/threads.py", line 201, in _toplevelTryFunc
    t.status = func(*args, **kwargs)
  File "/Documents/pyv/py3/lib/python3.7/site-packages/visidata/loaders/html.py", line 124, in save_html
    fp.write(html.escape(val))
  File "/usr/local/Cellar/python/3.7.6_1/Frameworks/Python.framework/Versions/3.7/lib/python3.7/html/__init__.py", line 19, in escape
    s = s.replace("&", "&amp;") # Must be done first!
AttributeError: 'int' object has no attribute 'replace'
```

Resolving patch:
diff --git a/visidata/loaders/html.py b/visidata/loaders/html.py
--- a/visidata/loaders/html.py
+++ b/visidata/loaders/html.py
@@ -121,7 +121,7 @@ def save_html(vd, p, *vsheets):
                     fp.write('<tr>')
                     for col, val in typedvals.items():
                         fp.write('<td>')
-                        fp.write(html.escape(val))
+                        fp.write(html.escape(str(val)))
                         fp.write('</td>')
                     fp.write('</tr>\n')
 


Test patch: