New run_sanity_checks mechanism, for SpatiLite

Moved VirtualSpatialIndex check into a new mechanism that should allow
us to add further sanity checks in the future.

To test this I've had to commit a binary sample SpatiaLite database to
the repository. I included a build script for creating that database.

Closes #466
pull/479/head
Simon Willison 2019-05-11 15:55:30 -07:00
rodzic c692cd2911
commit da0b3ce2b7
5 zmienionych plików z 68 dodań i 26 usunięć

5
.gitignore vendored
Wyświetl plik

@ -9,9 +9,8 @@ scratchpad
Pipfile
Pipfile.lock
# SQLite databases
*.db
*.sqlite
fixtures.db
*test.db
# Byte-compiled / optimized / DLL files
__pycache__/

Wyświetl plik

@ -344,6 +344,28 @@ class Datasette:
except ValueError:
# Plugin already registered
pass
# Run the sanity checks
asyncio.get_event_loop().run_until_complete(self.run_sanity_checks())
async def run_sanity_checks(self):
# Only one check right now, for Spatialite
for database_name, database in self.databases.items():
# Run pragma_info on every table
for table in await database.table_names():
try:
await self.execute(
database_name,
"PRAGMA table_info({});".format(escape_sqlite(table)),
)
except sqlite3.OperationalError as e:
if e.args[0] == "no such module: VirtualSpatialIndex":
raise click.UsageError(
"It looks like you're trying to load a SpatiaLite"
" database without first loading the SpatiaLite module."
"\n\nRead more: https://datasette.readthedocs.io/en/latest/spatialite.html"
)
else:
raise
def config(self, key):
return self._config.get(key, None)
@ -530,29 +552,17 @@ class Datasette:
name = path.stem
if name in self._inspect:
raise Exception("Multiple files with same stem %s" % name)
try:
with sqlite3.connect(
"file:{}?mode=ro".format(path), uri=True
) as conn:
self.prepare_connection(conn)
self._inspect[name] = {
"hash": inspect_hash(path),
"file": str(path),
"size": path.stat().st_size,
"views": inspect_views(conn),
"tables": inspect_tables(
conn, (self.metadata("databases") or {}).get(name, {})
),
}
except sqlite3.OperationalError as e:
if e.args[0] == "no such module: VirtualSpatialIndex":
raise click.UsageError(
"It looks like you're trying to load a SpatiaLite"
" database without first loading the SpatiaLite module."
"\n\nRead more: https://datasette.readthedocs.io/en/latest/spatialite.html"
)
else:
raise
with sqlite3.connect("file:{}?mode=ro".format(path), uri=True) as conn:
self.prepare_connection(conn)
self._inspect[name] = {
"hash": inspect_hash(path),
"file": str(path),
"size": path.stat().st_size,
"views": inspect_views(conn),
"tables": inspect_tables(
conn, (self.metadata("databases") or {}).get(name, {})
),
}
return self._inspect
def register_custom_units(self):

Wyświetl plik

@ -0,0 +1,23 @@
import sqlite3
# This script generates the spatialite.db file in our tests directory.
def generate_it(filename):
conn = sqlite3.connect(filename)
# Lead the spatialite extension:
conn.enable_load_extension(True)
conn.load_extension("/usr/local/lib/mod_spatialite.dylib")
conn.execute("select InitSpatialMetadata(1)")
conn.executescript("create table museums (name text)")
conn.execute("SELECT AddGeometryColumn('museums', 'point_geom', 4326, 'POINT', 2);")
# At this point it is around 5MB - we can shrink it dramatically by doing thisO
conn.execute("delete from spatial_ref_sys")
conn.execute("delete from spatial_ref_sys_aux")
conn.commit()
conn.execute("vacuum")
conn.close()
if __name__ == "__main__":
generate_it("spatialite.db")

BIN
tests/spatialite.db 100644

Plik binarny nie jest wyświetlany.

Wyświetl plik

@ -1,6 +1,7 @@
from .fixtures import app_client
from datasette.cli import cli
from click.testing import CliRunner
import pathlib
import json
@ -28,3 +29,12 @@ def test_inspect_cli_writes_to_file(app_client):
assert 0 == result.exit_code, result.output
data = json.load(open("foo.json"))
assert ["fixtures"] == list(data.keys())
def test_spatialite_error_if_attempt_to_open_spatialite():
runner = CliRunner()
result = runner.invoke(
cli, ["serve", str(pathlib.Path(__file__).parent / "spatialite.db")]
)
assert result.exit_code != 0
assert "trying to load a SpatiaLite database" in result.output