Skip to content

Commit d19bf10

Browse files
committed
Use hex for blob data output format. b64 is available w/ --base64
Note: to use with import/export the latest Peewee verison is required. Fixes #214
1 parent 605323c commit d19bf10

2 files changed

Lines changed: 51 additions & 40 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ The following options are available:
127127
* `-Q`, `--query-rows-per-page`: set pagination on query page, default 1000 rows.
128128
* `-T`, `--no-truncate`: disable ellipsis for long text values. If this option
129129
is used, the full text value is always shown.
130+
* `-B`, `--base64`: BLOB data as base64 (default is hex).
130131
* `-e`, `--extension`: path or name of loadable extension(s). To load
131132
multiple extensions, specify ``-e [path]`` for each extension.
132133
* `-s`, `--startup-hook`: path to a startup hook used to initialize the

sqlite_web/sqlite_web.py

Lines changed: 50 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -18,29 +18,15 @@
1818
import time
1919
import webbrowser
2020
from collections import namedtuple, OrderedDict
21+
from functools import reduce
2122
from functools import wraps
2223
from getpass import getpass
24+
from io import StringIO
2325
from io import TextIOWrapper
2426
from logging.handlers import WatchedFileHandler
2527
from werkzeug.routing import BaseConverter
2628
from werkzeug.utils import secure_filename
2729

28-
# Py2k compat.
29-
if sys.version_info[0] == 2:
30-
PY2 = True
31-
binary_types = (buffer, bytes, bytearray)
32-
decode_handler = 'replace'
33-
numeric = (int, long, float)
34-
unicode_type = unicode
35-
from StringIO import StringIO
36-
else:
37-
PY2 = False
38-
binary_types = (bytes, bytearray)
39-
decode_handler = 'backslashreplace'
40-
numeric = (int, float)
41-
unicode_type = str
42-
from functools import reduce
43-
from io import StringIO
4430

4531
try:
4632
from flask import (
@@ -90,6 +76,8 @@ def syntax_highlight(data):
9076

9177
CUR_DIR = os.path.realpath(os.path.dirname(__file__))
9278
DEBUG = False
79+
80+
BLOB_AS_BASE64 = False # Default is hex.
9381
ROWS_PER_PAGE = 50
9482
QUERY_ROWS_PER_PAGE = 1000
9583
TRUNCATE_VALUES = True
@@ -887,10 +875,16 @@ def minimal_validate_field(field, value):
887875
return value, 'Value must be 1, 0, true, false, t or f.'
888876
value = True if value.lower() in ('1', 't', 'true') else False
889877
elif isinstance(field, BlobField):
890-
try:
891-
value = base64.b64decode(value)
892-
except Exception as exc:
893-
return value, 'Value must be base64-encoded binary data.'
878+
if app.config['BLOB_AS_BASE64']:
879+
try:
880+
value = base64.b64decode(value)
881+
except Exception as exc:
882+
return value, 'Value must be base64-encoded binary data.'
883+
else:
884+
try:
885+
value = bytes.fromhex(value)
886+
except Exception as exc:
887+
return value, 'Value must be valid hex representation.'
894888
try:
895889
field.db_value(value)
896890
except Exception as exc:
@@ -1014,7 +1008,10 @@ def table_update(table, pk):
10141008
if value is None:
10151009
row[field.name] = None
10161010
elif isinstance(field, BlobField):
1017-
row[field.name] = base64.b64encode(value).decode('utf8')
1011+
if app.config['BLOB_AS_BASE64']:
1012+
row[field.name] = base64.b64encode(value).decode('utf8')
1013+
else:
1014+
row[field.name] = value.hex()
10181015
else:
10191016
row[field.name] = value
10201017

@@ -1128,6 +1125,9 @@ def export(query, export_format, table=None):
11281125
# Avoid any special chars in export filename.
11291126
filename = re.sub(r'[^\w\d\-\.]+', '', filename)
11301127

1128+
if peewee_version >= (4, 0, 2):
1129+
kwargs['base64_bytes'] = app.config['BLOB_AS_BASE64']
1130+
11311131
dataset.freeze(query, export_format, file_obj=buf, **kwargs)
11321132

11331133
response_data = buf.getvalue()
@@ -1193,25 +1193,26 @@ def table_import(table):
11931193
# compatible with Python's CSV module. We'd need to reach pretty
11941194
# far into Flask's internals to modify this behavior, so instead
11951195
# we'll just translate the stream into utf8-decoded unicode.
1196-
if not PY2:
1197-
try:
1198-
stream = TextIOWrapper(file_obj, encoding='utf8')
1199-
except AttributeError:
1200-
# The SpooledTemporaryFile used by werkzeug does not
1201-
# implement an API that the TextIOWrapper expects, so we'll
1202-
# just consume the whole damn thing and decode it.
1203-
# Fixed in werkzeug 0.15.
1204-
stream = StringIO(file_obj.read().decode('utf8'))
1205-
else:
1206-
stream = file_obj.stream
1207-
1196+
try:
1197+
stream = TextIOWrapper(file_obj, encoding='utf8')
1198+
except AttributeError:
1199+
# The SpooledTemporaryFile used by werkzeug does not
1200+
# implement an API that the TextIOWrapper expects, so we'll
1201+
# just consume the whole damn thing and decode it.
1202+
# Fixed in werkzeug 0.15.
1203+
stream = StringIO(file_obj.read().decode('utf8'))
1204+
1205+
kwargs = {}
1206+
if peewee_version >= (4, 0, 2):
1207+
kwargs['base64_bytes'] = app.config['BLOB_AS_BASE64']
12081208
try:
12091209
with dataset.transaction():
12101210
count = dataset.thaw(
12111211
table,
12121212
format=format,
12131213
file_obj=stream,
1214-
strict=strict)
1214+
strict=strict,
1215+
**kwargs)
12151216
except Exception as exc:
12161217
flash('Error importing file: %s' % exc, 'danger')
12171218
app.logger.exception('Error importing file.')
@@ -1292,17 +1293,18 @@ def pk_display(table_pk, pk):
12921293

12931294
@app.template_filter('value_filter')
12941295
def value_filter(value, max_length=50):
1295-
if isinstance(value, numeric):
1296+
if isinstance(value, (int, float)):
12961297
return value
12971298

1298-
if isinstance(value, binary_types):
1299-
if not isinstance(value, (bytes, bytearray)):
1300-
value = bytes(value) # Handle `buffer` type.
1299+
if isinstance(value, (bytes, bytearray, memoryview)):
13011300
try:
13021301
value = value.decode('utf8')
13031302
except UnicodeDecodeError:
1304-
value = base64.b64encode(value)[:1024].decode('utf8')
1305-
if isinstance(value, unicode_type):
1303+
if app.config['BLOB_AS_BASE64']:
1304+
value = base64.b64encode(value)[:1024].decode('utf8')
1305+
else:
1306+
value = value.hex()[:1024]
1307+
if isinstance(value, str):
13061308
if link_re.match(value):
13071309
label = value
13081310
if len(value) > max_length and app.config['TRUNCATE_VALUES']:
@@ -1481,6 +1483,12 @@ def get_option_parser():
14811483
help=('Disable truncating long text values. By default text values '
14821484
'are ellipsized after 50 characters and the full text is shown '
14831485
'on click.'))
1486+
parser.add_option(
1487+
'-B',
1488+
'--base64',
1489+
action='store_true',
1490+
dest='base64',
1491+
help='BLOB data as base64 (default is hex)')
14841492
parser.add_option(
14851493
'-u',
14861494
'--url-prefix',
@@ -1667,6 +1675,8 @@ def configure_app():
16671675
app.config['ROWS_PER_PAGE'] = options.rows_per_page
16681676
if options.query_rows_per_page:
16691677
app.config['QUERY_ROWS_PER_PAGE'] = options.query_rows_per_page
1678+
if options.base64:
1679+
app.config['BLOB_AS_BASE64'] = options.base64
16701680

16711681
app.config['TRUNCATE_VALUES'] = options.truncate_values
16721682

0 commit comments

Comments
 (0)