mirror of
https://github.com/simonw/datasette.git
synced 2025-12-10 16:51:24 +01:00
Ran black source formatting tool against new views/ and app.py
This commit is contained in:
parent
f4f88ce525
commit
eced084a6b
5 changed files with 737 additions and 627 deletions
|
|
@ -27,6 +27,7 @@ HASH_LENGTH = 7
|
|||
|
||||
|
||||
class DatasetteError(Exception):
|
||||
|
||||
def __init__(self, message, title=None, error_dict=None, status=500, template=None):
|
||||
self.message = message
|
||||
self.title = title
|
||||
|
|
@ -35,25 +36,29 @@ class DatasetteError(Exception):
|
|||
|
||||
|
||||
class RenderMixin(HTTPMethodView):
|
||||
|
||||
def render(self, templates, **context):
|
||||
template = self.jinja_env.select_template(templates)
|
||||
select_templates = ['{}{}'.format(
|
||||
'*' if template_name == template.name else '',
|
||||
template_name
|
||||
) for template_name in templates]
|
||||
select_templates = [
|
||||
"{}{}".format("*" if template_name == template.name else "", template_name)
|
||||
for template_name in templates
|
||||
]
|
||||
return response.html(
|
||||
template.render({
|
||||
**context, **{
|
||||
'app_css_hash': self.ds.app_css_hash(),
|
||||
'select_templates': select_templates,
|
||||
'zip': zip,
|
||||
template.render(
|
||||
{
|
||||
**context,
|
||||
**{
|
||||
"app_css_hash": self.ds.app_css_hash(),
|
||||
"select_templates": select_templates,
|
||||
"zip": zip,
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
class BaseView(RenderMixin):
|
||||
re_named_parameter = re.compile(':([a-zA-Z0-9_]+)')
|
||||
re_named_parameter = re.compile(":([a-zA-Z0-9_]+)")
|
||||
|
||||
def __init__(self, datasette):
|
||||
self.ds = datasette
|
||||
|
|
@ -65,35 +70,35 @@ class BaseView(RenderMixin):
|
|||
|
||||
def table_metadata(self, database, table):
|
||||
"Fetch table-specific metadata."
|
||||
return self.ds.metadata.get(
|
||||
'databases', {}
|
||||
).get(database, {}).get('tables', {}).get(table, {})
|
||||
return self.ds.metadata.get("databases", {}).get(database, {}).get(
|
||||
"tables", {}
|
||||
).get(
|
||||
table, {}
|
||||
)
|
||||
|
||||
def options(self, request, *args, **kwargs):
|
||||
r = response.text('ok')
|
||||
r = response.text("ok")
|
||||
if self.ds.cors:
|
||||
r.headers['Access-Control-Allow-Origin'] = '*'
|
||||
r.headers["Access-Control-Allow-Origin"] = "*"
|
||||
return r
|
||||
|
||||
def redirect(self, request, path, forward_querystring=True):
|
||||
if request.query_string and '?' not in path and forward_querystring:
|
||||
path = '{}?{}'.format(
|
||||
path, request.query_string
|
||||
)
|
||||
if request.query_string and "?" not in path and forward_querystring:
|
||||
path = "{}?{}".format(path, request.query_string)
|
||||
r = response.redirect(path)
|
||||
r.headers['Link'] = '<{}>; rel=preload'.format(path)
|
||||
r.headers["Link"] = "<{}>; rel=preload".format(path)
|
||||
if self.ds.cors:
|
||||
r.headers['Access-Control-Allow-Origin'] = '*'
|
||||
r.headers["Access-Control-Allow-Origin"] = "*"
|
||||
return r
|
||||
|
||||
def resolve_db_name(self, db_name, **kwargs):
|
||||
databases = self.ds.inspect()
|
||||
hash = None
|
||||
name = None
|
||||
if '-' in db_name:
|
||||
if "-" in db_name:
|
||||
# Might be name-and-hash, or might just be
|
||||
# a name with a hyphen in it
|
||||
name, hash = db_name.rsplit('-', 1)
|
||||
name, hash = db_name.rsplit("-", 1)
|
||||
if name not in databases:
|
||||
# Try the whole name
|
||||
name = db_name
|
||||
|
|
@ -104,24 +109,32 @@ class BaseView(RenderMixin):
|
|||
try:
|
||||
info = databases[name]
|
||||
except KeyError:
|
||||
raise NotFound('Database not found: {}'.format(name))
|
||||
expected = info['hash'][:HASH_LENGTH]
|
||||
raise NotFound("Database not found: {}".format(name))
|
||||
|
||||
expected = info["hash"][:HASH_LENGTH]
|
||||
if expected != hash:
|
||||
should_redirect = '/{}-{}'.format(
|
||||
name, expected,
|
||||
)
|
||||
if 'table' in kwargs:
|
||||
should_redirect += '/' + kwargs['table']
|
||||
if 'pk_path' in kwargs:
|
||||
should_redirect += '/' + kwargs['pk_path']
|
||||
if 'as_json' in kwargs:
|
||||
should_redirect += kwargs['as_json']
|
||||
if 'as_db' in kwargs:
|
||||
should_redirect += kwargs['as_db']
|
||||
should_redirect = "/{}-{}".format(name, expected)
|
||||
if "table" in kwargs:
|
||||
should_redirect += "/" + kwargs["table"]
|
||||
if "pk_path" in kwargs:
|
||||
should_redirect += "/" + kwargs["pk_path"]
|
||||
if "as_json" in kwargs:
|
||||
should_redirect += kwargs["as_json"]
|
||||
if "as_db" in kwargs:
|
||||
should_redirect += kwargs["as_db"]
|
||||
return name, expected, should_redirect
|
||||
|
||||
return name, expected, None
|
||||
|
||||
async def execute(self, db_name, sql, params=None, truncate=False, custom_time_limit=None, page_size=None):
|
||||
async def execute(
|
||||
self,
|
||||
db_name,
|
||||
sql,
|
||||
params=None,
|
||||
truncate=False,
|
||||
custom_time_limit=None,
|
||||
page_size=None,
|
||||
):
|
||||
"""Executes sql against db_name in a thread"""
|
||||
page_size = page_size or self.page_size
|
||||
|
||||
|
|
@ -130,7 +143,7 @@ class BaseView(RenderMixin):
|
|||
if not conn:
|
||||
info = self.ds.inspect()[db_name]
|
||||
conn = sqlite3.connect(
|
||||
'file:{}?immutable=1'.format(info['file']),
|
||||
"file:{}?immutable=1".format(info["file"]),
|
||||
uri=True,
|
||||
check_same_thread=False,
|
||||
)
|
||||
|
|
@ -156,12 +169,16 @@ class BaseView(RenderMixin):
|
|||
rows = cursor.fetchall()
|
||||
truncated = False
|
||||
except Exception as e:
|
||||
print('ERROR: conn={}, sql = {}, params = {}: {}'.format(
|
||||
conn, repr(sql), params, e
|
||||
))
|
||||
print(
|
||||
"ERROR: conn={}, sql = {}, params = {}: {}".format(
|
||||
conn, repr(sql), params, e
|
||||
)
|
||||
)
|
||||
raise
|
||||
|
||||
if truncate:
|
||||
return rows, truncated, cursor.description
|
||||
|
||||
else:
|
||||
return rows
|
||||
|
||||
|
|
@ -176,11 +193,12 @@ class BaseView(RenderMixin):
|
|||
name, hash, should_redirect = self.resolve_db_name(db_name, **kwargs)
|
||||
if should_redirect:
|
||||
return self.redirect(request, should_redirect)
|
||||
|
||||
return await self.view_get(request, name, hash, **kwargs)
|
||||
|
||||
async def view_get(self, request, name, hash, **kwargs):
|
||||
try:
|
||||
as_json = kwargs.pop('as_json')
|
||||
as_json = kwargs.pop("as_json")
|
||||
except KeyError:
|
||||
as_json = False
|
||||
extra_template_data = {}
|
||||
|
|
@ -193,84 +211,84 @@ class BaseView(RenderMixin):
|
|||
)
|
||||
if isinstance(response_or_template_contexts, response.HTTPResponse):
|
||||
return response_or_template_contexts
|
||||
|
||||
else:
|
||||
data, extra_template_data, templates = response_or_template_contexts
|
||||
except (sqlite3.OperationalError, InvalidSql) as e:
|
||||
raise DatasetteError(str(e), title='Invalid SQL', status=400)
|
||||
raise DatasetteError(str(e), title="Invalid SQL", status=400)
|
||||
|
||||
except (sqlite3.OperationalError) as e:
|
||||
raise DatasetteError(str(e))
|
||||
|
||||
except DatasetteError:
|
||||
raise
|
||||
|
||||
end = time.time()
|
||||
data['query_ms'] = (end - start) * 1000
|
||||
for key in ('source', 'source_url', 'license', 'license_url'):
|
||||
data["query_ms"] = (end - start) * 1000
|
||||
for key in ("source", "source_url", "license", "license_url"):
|
||||
value = self.ds.metadata.get(key)
|
||||
if value:
|
||||
data[key] = value
|
||||
if as_json:
|
||||
# Special case for .jsono extension - redirect to _shape=objects
|
||||
if as_json == '.jsono':
|
||||
if as_json == ".jsono":
|
||||
return self.redirect(
|
||||
request,
|
||||
path_with_added_args(
|
||||
request,
|
||||
{'_shape': 'objects'},
|
||||
path=request.path.rsplit('.jsono', 1)[0] + '.json'
|
||||
{"_shape": "objects"},
|
||||
path=request.path.rsplit(".jsono", 1)[0] + ".json",
|
||||
),
|
||||
forward_querystring=False
|
||||
forward_querystring=False,
|
||||
)
|
||||
|
||||
# Deal with the _shape option
|
||||
shape = request.args.get('_shape', 'arrays')
|
||||
if shape in ('objects', 'object', 'array'):
|
||||
columns = data.get('columns')
|
||||
rows = data.get('rows')
|
||||
shape = request.args.get("_shape", "arrays")
|
||||
if shape in ("objects", "object", "array"):
|
||||
columns = data.get("columns")
|
||||
rows = data.get("rows")
|
||||
if rows and columns:
|
||||
data['rows'] = [
|
||||
dict(zip(columns, row))
|
||||
for row in rows
|
||||
]
|
||||
if shape == 'object':
|
||||
data["rows"] = [dict(zip(columns, row)) for row in rows]
|
||||
if shape == "object":
|
||||
error = None
|
||||
if 'primary_keys' not in data:
|
||||
error = '_shape=object is only available on tables'
|
||||
if "primary_keys" not in data:
|
||||
error = "_shape=object is only available on tables"
|
||||
else:
|
||||
pks = data['primary_keys']
|
||||
pks = data["primary_keys"]
|
||||
if not pks:
|
||||
error = '_shape=object not available for tables with no primary keys'
|
||||
error = "_shape=object not available for tables with no primary keys"
|
||||
else:
|
||||
object_rows = {}
|
||||
for row in data['rows']:
|
||||
for row in data["rows"]:
|
||||
pk_string = path_from_row_pks(row, pks, not pks)
|
||||
object_rows[pk_string] = row
|
||||
data = object_rows
|
||||
if error:
|
||||
data = {
|
||||
'ok': False,
|
||||
'error': error,
|
||||
'database': name,
|
||||
'database_hash': hash,
|
||||
"ok": False,
|
||||
"error": error,
|
||||
"database": name,
|
||||
"database_hash": hash,
|
||||
}
|
||||
elif shape == 'array':
|
||||
data = data['rows']
|
||||
elif shape == 'arrays':
|
||||
elif shape == "array":
|
||||
data = data["rows"]
|
||||
elif shape == "arrays":
|
||||
pass
|
||||
else:
|
||||
status_code = 400
|
||||
data = {
|
||||
'ok': False,
|
||||
'error': 'Invalid _shape: {}'.format(shape),
|
||||
'status': 400,
|
||||
'title': None,
|
||||
"ok": False,
|
||||
"error": "Invalid _shape: {}".format(shape),
|
||||
"status": 400,
|
||||
"title": None,
|
||||
}
|
||||
headers = {}
|
||||
if self.ds.cors:
|
||||
headers['Access-Control-Allow-Origin'] = '*'
|
||||
headers["Access-Control-Allow-Origin"] = "*"
|
||||
r = response.HTTPResponse(
|
||||
json.dumps(
|
||||
data, cls=CustomJSONEncoder
|
||||
),
|
||||
json.dumps(data, cls=CustomJSONEncoder),
|
||||
status=status_code,
|
||||
content_type='application/json',
|
||||
content_type="application/json",
|
||||
headers=headers,
|
||||
)
|
||||
else:
|
||||
|
|
@ -285,72 +303,69 @@ class BaseView(RenderMixin):
|
|||
**data,
|
||||
**extras,
|
||||
**{
|
||||
'url_json': path_with_ext(request, '.json'),
|
||||
'url_jsono': path_with_ext(request, '.jsono'),
|
||||
'extra_css_urls': self.ds.extra_css_urls(),
|
||||
'extra_js_urls': self.ds.extra_js_urls(),
|
||||
'datasette_version': __version__,
|
||||
"url_json": path_with_ext(request, ".json"),
|
||||
"url_jsono": path_with_ext(request, ".jsono"),
|
||||
"extra_css_urls": self.ds.extra_css_urls(),
|
||||
"extra_js_urls": self.ds.extra_js_urls(),
|
||||
"datasette_version": __version__,
|
||||
}
|
||||
}
|
||||
if 'metadata' not in context:
|
||||
context['metadata'] = self.ds.metadata
|
||||
r = self.render(
|
||||
templates,
|
||||
**context,
|
||||
)
|
||||
if "metadata" not in context:
|
||||
context["metadata"] = self.ds.metadata
|
||||
r = self.render(templates, **context)
|
||||
r.status = status_code
|
||||
# Set far-future cache expiry
|
||||
if self.ds.cache_headers:
|
||||
r.headers['Cache-Control'] = 'max-age={}'.format(
|
||||
365 * 24 * 60 * 60
|
||||
)
|
||||
r.headers["Cache-Control"] = "max-age={}".format(365 * 24 * 60 * 60)
|
||||
return r
|
||||
|
||||
async def custom_sql(self, request, name, hash, sql, editable=True, canned_query=None):
|
||||
async def custom_sql(
|
||||
self, request, name, hash, sql, editable=True, canned_query=None
|
||||
):
|
||||
params = request.raw_args
|
||||
if 'sql' in params:
|
||||
params.pop('sql')
|
||||
if '_shape' in params:
|
||||
params.pop('_shape')
|
||||
if "sql" in params:
|
||||
params.pop("sql")
|
||||
if "_shape" in params:
|
||||
params.pop("_shape")
|
||||
# Extract any :named parameters
|
||||
named_parameters = self.re_named_parameter.findall(sql)
|
||||
named_parameter_values = {
|
||||
named_parameter: params.get(named_parameter) or ''
|
||||
named_parameter: params.get(named_parameter) or ""
|
||||
for named_parameter in named_parameters
|
||||
}
|
||||
|
||||
# Set to blank string if missing from params
|
||||
for named_parameter in named_parameters:
|
||||
if named_parameter not in params:
|
||||
params[named_parameter] = ''
|
||||
params[named_parameter] = ""
|
||||
|
||||
extra_args = {}
|
||||
if params.get('_timelimit'):
|
||||
extra_args['custom_time_limit'] = int(params['_timelimit'])
|
||||
if params.get("_timelimit"):
|
||||
extra_args["custom_time_limit"] = int(params["_timelimit"])
|
||||
rows, truncated, description = await self.execute(
|
||||
name, sql, params, truncate=True, **extra_args
|
||||
)
|
||||
columns = [r[0] for r in description]
|
||||
|
||||
templates = ['query-{}.html'.format(to_css_class(name)), 'query.html']
|
||||
templates = ["query-{}.html".format(to_css_class(name)), "query.html"]
|
||||
if canned_query:
|
||||
templates.insert(0, 'query-{}-{}.html'.format(
|
||||
to_css_class(name), to_css_class(canned_query)
|
||||
))
|
||||
templates.insert(
|
||||
0,
|
||||
"query-{}-{}.html".format(
|
||||
to_css_class(name), to_css_class(canned_query)
|
||||
),
|
||||
)
|
||||
|
||||
return {
|
||||
'database': name,
|
||||
'rows': rows,
|
||||
'truncated': truncated,
|
||||
'columns': columns,
|
||||
'query': {
|
||||
'sql': sql,
|
||||
'params': params,
|
||||
}
|
||||
"database": name,
|
||||
"rows": rows,
|
||||
"truncated": truncated,
|
||||
"columns": columns,
|
||||
"query": {"sql": sql, "params": params},
|
||||
}, {
|
||||
'database_hash': hash,
|
||||
'custom_sql': True,
|
||||
'named_parameter_values': named_parameter_values,
|
||||
'editable': editable,
|
||||
'canned_query': canned_query,
|
||||
"database_hash": hash,
|
||||
"custom_sql": True,
|
||||
"named_parameter_values": named_parameter_values,
|
||||
"editable": editable,
|
||||
"canned_query": canned_query,
|
||||
}, templates
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue