kopia lustrzana https://github.com/simonw/datasette
Added /-/metadata /-/plugins /-/inspect, closes #225
rodzic
6e28ed447f
commit
b55809a1e2
|
@ -17,7 +17,6 @@ import json
|
||||||
import jinja2
|
import jinja2
|
||||||
import hashlib
|
import hashlib
|
||||||
import time
|
import time
|
||||||
import pkg_resources
|
|
||||||
import pint
|
import pint
|
||||||
import pluggy
|
import pluggy
|
||||||
import traceback
|
import traceback
|
||||||
|
@ -31,6 +30,7 @@ from .utils import (
|
||||||
escape_sqlite,
|
escape_sqlite,
|
||||||
filters_should_redirect,
|
filters_should_redirect,
|
||||||
get_all_foreign_keys,
|
get_all_foreign_keys,
|
||||||
|
get_plugins,
|
||||||
is_url,
|
is_url,
|
||||||
InvalidSql,
|
InvalidSql,
|
||||||
module_from_path,
|
module_from_path,
|
||||||
|
@ -402,15 +402,16 @@ class IndexView(RenderMixin):
|
||||||
}
|
}
|
||||||
databases.append(database)
|
databases.append(database)
|
||||||
if as_json:
|
if as_json:
|
||||||
|
headers = {}
|
||||||
|
if self.ds.cors:
|
||||||
|
headers['Access-Control-Allow-Origin'] = '*'
|
||||||
return response.HTTPResponse(
|
return response.HTTPResponse(
|
||||||
json.dumps(
|
json.dumps(
|
||||||
{db['name']: db for db in databases},
|
{db['name']: db for db in databases},
|
||||||
cls=CustomJSONEncoder
|
cls=CustomJSONEncoder
|
||||||
),
|
),
|
||||||
content_type='application/json',
|
content_type='application/json',
|
||||||
headers={
|
headers=headers,
|
||||||
'Access-Control-Allow-Origin': '*'
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
return self.render(
|
return self.render(
|
||||||
|
@ -423,6 +424,32 @@ class IndexView(RenderMixin):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class JsonDataView(RenderMixin):
|
||||||
|
def __init__(self, datasette, filename, data_callback):
|
||||||
|
self.ds = datasette
|
||||||
|
self.jinja_env = datasette.jinja_env
|
||||||
|
self.filename = filename
|
||||||
|
self.data_callback = data_callback
|
||||||
|
|
||||||
|
async def get(self, request, as_json):
|
||||||
|
data = self.data_callback()
|
||||||
|
if as_json:
|
||||||
|
headers = {}
|
||||||
|
if self.ds.cors:
|
||||||
|
headers['Access-Control-Allow-Origin'] = '*'
|
||||||
|
return response.HTTPResponse(
|
||||||
|
json.dumps(data),
|
||||||
|
content_type='application/json',
|
||||||
|
headers=headers,
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return self.render(
|
||||||
|
['show_json.html'],
|
||||||
|
filename=self.filename,
|
||||||
|
data=data,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
async def favicon(request):
|
async def favicon(request):
|
||||||
return response.text('')
|
return response.text('')
|
||||||
|
|
||||||
|
@ -1302,15 +1329,25 @@ class Datasette:
|
||||||
for path, dirname in self.static_mounts:
|
for path, dirname in self.static_mounts:
|
||||||
app.static(path, dirname)
|
app.static(path, dirname)
|
||||||
# Mount any plugin static/ directories
|
# Mount any plugin static/ directories
|
||||||
for plugin_module in pm.get_plugins():
|
for plugin in get_plugins(pm):
|
||||||
try:
|
if plugin['static_path']:
|
||||||
if pkg_resources.resource_isdir(plugin_module.__name__, 'static'):
|
modpath = '/-/static-plugins/{}/'.format(plugin['name'])
|
||||||
modpath = '/-/static-plugins/{}/'.format(plugin_module.__name__)
|
app.static(modpath, plugin['static_path'])
|
||||||
dirpath = pkg_resources.resource_filename(plugin_module.__name__, 'static')
|
app.add_route(
|
||||||
app.static(modpath, dirpath)
|
JsonDataView.as_view(self, 'inspect.json', lambda: self.inspect()),
|
||||||
except (KeyError, ImportError):
|
'/-/inspect<as_json:(\.json)?$>'
|
||||||
# Caused by --plugins_dir= plugins - KeyError/ImportError thrown in Py3.5
|
)
|
||||||
pass
|
app.add_route(
|
||||||
|
JsonDataView.as_view(self, 'metadata.json', lambda: self.metadata),
|
||||||
|
'/-/metadata<as_json:(\.json)?$>'
|
||||||
|
)
|
||||||
|
app.add_route(
|
||||||
|
JsonDataView.as_view(self, 'plugins.json', lambda: [{
|
||||||
|
'name': p['name'],
|
||||||
|
'static': p['static_path'] is not None
|
||||||
|
} for p in get_plugins(pm)]),
|
||||||
|
'/-/plugins<as_json:(\.json)?$>'
|
||||||
|
)
|
||||||
app.add_route(
|
app.add_route(
|
||||||
DatabaseView.as_view(self),
|
DatabaseView.as_view(self),
|
||||||
'/<db_name:[^/\.]+?><as_json:(\.jsono?)?$>'
|
'/<db_name:[^/\.]+?><as_json:(\.jsono?)?$>'
|
||||||
|
|
|
@ -0,0 +1,12 @@
|
||||||
|
{% extends "base.html" %}
|
||||||
|
|
||||||
|
{% block title %}{{ filename }}{% endblock %}
|
||||||
|
|
||||||
|
{% block body_class %}show-json{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<h1>{{ filename }}</h1>
|
||||||
|
|
||||||
|
<pre>{{ data|tojson(indent=4) }}</pre>
|
||||||
|
|
||||||
|
{% endblock %}
|
|
@ -4,6 +4,7 @@ import hashlib
|
||||||
import imp
|
import imp
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
import pkg_resources
|
||||||
import re
|
import re
|
||||||
import shlex
|
import shlex
|
||||||
import sqlite3
|
import sqlite3
|
||||||
|
@ -683,3 +684,20 @@ def module_from_path(path, name):
|
||||||
code = compile(file.read(), path, 'exec', dont_inherit=True)
|
code = compile(file.read(), path, 'exec', dont_inherit=True)
|
||||||
exec(code, mod.__dict__)
|
exec(code, mod.__dict__)
|
||||||
return mod
|
return mod
|
||||||
|
|
||||||
|
|
||||||
|
def get_plugins(pm):
|
||||||
|
plugins = []
|
||||||
|
for plugin in pm.get_plugins():
|
||||||
|
static_path = None
|
||||||
|
try:
|
||||||
|
if pkg_resources.resource_isdir(plugin.__name__, 'static'):
|
||||||
|
static_path = pkg_resources.resource_filename(plugin.__name__, 'static')
|
||||||
|
except (KeyError, ImportError):
|
||||||
|
# Caused by --plugins_dir= plugins - KeyError/ImportError thrown in Py3.5
|
||||||
|
pass
|
||||||
|
plugins.append({
|
||||||
|
'name': plugin.__name__,
|
||||||
|
'static_path': static_path,
|
||||||
|
})
|
||||||
|
return plugins
|
||||||
|
|
|
@ -29,7 +29,9 @@ def app_client(sql_time_limit_ms=None):
|
||||||
ds.sqlite_functions.append(
|
ds.sqlite_functions.append(
|
||||||
('sleep', 1, lambda n: time.sleep(float(n))),
|
('sleep', 1, lambda n: time.sleep(float(n))),
|
||||||
)
|
)
|
||||||
yield ds.app().test_client
|
client = ds.app().test_client
|
||||||
|
client.ds = ds
|
||||||
|
yield client
|
||||||
|
|
||||||
|
|
||||||
def app_client_longer_time_limit():
|
def app_client_longer_time_limit():
|
||||||
|
|
|
@ -3,6 +3,7 @@ from .fixtures import (
|
||||||
app_client_longer_time_limit,
|
app_client_longer_time_limit,
|
||||||
generate_compound_rows,
|
generate_compound_rows,
|
||||||
generate_sortable_rows,
|
generate_sortable_rows,
|
||||||
|
METADATA,
|
||||||
)
|
)
|
||||||
import pytest
|
import pytest
|
||||||
|
|
||||||
|
@ -629,3 +630,30 @@ def test_plugins_dir_plugin(app_client):
|
||||||
gather_request=False
|
gather_request=False
|
||||||
)
|
)
|
||||||
assert pytest.approx(328.0839) == response.json['rows'][0][0]
|
assert pytest.approx(328.0839) == response.json['rows'][0][0]
|
||||||
|
|
||||||
|
|
||||||
|
def test_metadata_json(app_client):
|
||||||
|
response = app_client.get(
|
||||||
|
"/-/metadata.json",
|
||||||
|
gather_request=False
|
||||||
|
)
|
||||||
|
assert METADATA == response.json
|
||||||
|
|
||||||
|
|
||||||
|
def test_inspect_json(app_client):
|
||||||
|
response = app_client.get(
|
||||||
|
"/-/inspect.json",
|
||||||
|
gather_request=False
|
||||||
|
)
|
||||||
|
assert app_client.ds.inspect() == response.json
|
||||||
|
|
||||||
|
|
||||||
|
def test_plugins_json(app_client):
|
||||||
|
response = app_client.get(
|
||||||
|
"/-/plugins.json",
|
||||||
|
gather_request=False
|
||||||
|
)
|
||||||
|
# This will include any plugins that have been installed into the
|
||||||
|
# current virtual environment, so we only check for the presence of
|
||||||
|
# the one we know will definitely be There
|
||||||
|
assert {'name': 'my_plugin.py', 'static': False} in response.json
|
||||||
|
|
Ładowanie…
Reference in New Issue