mirror of
https://github.com/simonw/datasette.git
synced 2025-12-10 16:51:24 +01:00
--dirs scan mechanism, work in progress - refs #417
This commit is contained in:
parent
6aa516d82d
commit
6ff261c1de
4 changed files with 55 additions and 0 deletions
|
|
@ -31,6 +31,8 @@ from .utils import (
|
||||||
escape_css_string,
|
escape_css_string,
|
||||||
escape_sqlite,
|
escape_sqlite,
|
||||||
format_bytes,
|
format_bytes,
|
||||||
|
is_valid_sqlite,
|
||||||
|
get_plugins,
|
||||||
module_from_path,
|
module_from_path,
|
||||||
sqlite3,
|
sqlite3,
|
||||||
to_css_class,
|
to_css_class,
|
||||||
|
|
@ -149,6 +151,7 @@ class Datasette:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
files,
|
files,
|
||||||
|
dirs=None,
|
||||||
immutables=None,
|
immutables=None,
|
||||||
cache_headers=True,
|
cache_headers=True,
|
||||||
cors=False,
|
cors=False,
|
||||||
|
|
@ -163,6 +166,7 @@ class Datasette:
|
||||||
version_note=None,
|
version_note=None,
|
||||||
):
|
):
|
||||||
immutables = immutables or []
|
immutables = immutables or []
|
||||||
|
self.dirs = dirs or []
|
||||||
self.files = tuple(files) + tuple(immutables)
|
self.files = tuple(files) + tuple(immutables)
|
||||||
self.immutables = set(immutables)
|
self.immutables = set(immutables)
|
||||||
if not self.files:
|
if not self.files:
|
||||||
|
|
@ -182,6 +186,7 @@ class Datasette:
|
||||||
if db.name in self.databases:
|
if db.name in self.databases:
|
||||||
raise Exception("Multiple files with same stem: {}".format(db.name))
|
raise Exception("Multiple files with same stem: {}".format(db.name))
|
||||||
self.add_database(db.name, db)
|
self.add_database(db.name, db)
|
||||||
|
self.scan_dirs()
|
||||||
self.cache_headers = cache_headers
|
self.cache_headers = cache_headers
|
||||||
self.cors = cors
|
self.cors = cors
|
||||||
self._metadata = metadata or {}
|
self._metadata = metadata or {}
|
||||||
|
|
@ -217,6 +222,25 @@ class Datasette:
|
||||||
def remove_database(self, name):
|
def remove_database(self, name):
|
||||||
self.databases.pop(name)
|
self.databases.pop(name)
|
||||||
|
|
||||||
|
def scan_dirs(self):
|
||||||
|
# Recurse through self.dirs looking for new SQLite DBs
|
||||||
|
i = 0
|
||||||
|
for dir in self.dirs:
|
||||||
|
print(dir)
|
||||||
|
for filepath in Path(dir).glob("**/*.db"):
|
||||||
|
print(filepath)
|
||||||
|
if is_valid_sqlite(filepath):
|
||||||
|
self.add_database(
|
||||||
|
str(filepath)
|
||||||
|
.replace("../", "")
|
||||||
|
.replace("/", "_")
|
||||||
|
.replace(".db", ""),
|
||||||
|
Database(self, filepath, is_mutable=True),
|
||||||
|
)
|
||||||
|
i += 1
|
||||||
|
if i >= 20:
|
||||||
|
break
|
||||||
|
|
||||||
def config(self, key):
|
def config(self, key):
|
||||||
return self._config.get(key, None)
|
return self._config.get(key, None)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -232,6 +232,13 @@ def package(
|
||||||
|
|
||||||
@cli.command()
|
@cli.command()
|
||||||
@click.argument("files", type=click.Path(exists=True), nargs=-1)
|
@click.argument("files", type=click.Path(exists=True), nargs=-1)
|
||||||
|
@click.option(
|
||||||
|
"-d",
|
||||||
|
"--dir",
|
||||||
|
type=click.Path(exists=True),
|
||||||
|
help="Directories to scan for SQLite files to serve",
|
||||||
|
multiple=True,
|
||||||
|
)
|
||||||
@click.option(
|
@click.option(
|
||||||
"-i",
|
"-i",
|
||||||
"--immutable",
|
"--immutable",
|
||||||
|
|
@ -310,6 +317,7 @@ def package(
|
||||||
@click.option("--help-config", is_flag=True, help="Show available config options")
|
@click.option("--help-config", is_flag=True, help="Show available config options")
|
||||||
def serve(
|
def serve(
|
||||||
files,
|
files,
|
||||||
|
dir,
|
||||||
immutable,
|
immutable,
|
||||||
host,
|
host,
|
||||||
port,
|
port,
|
||||||
|
|
@ -361,6 +369,7 @@ def serve(
|
||||||
)
|
)
|
||||||
ds = Datasette(
|
ds = Datasette(
|
||||||
files,
|
files,
|
||||||
|
dir,
|
||||||
immutables=immutable,
|
immutables=immutable,
|
||||||
cache_headers=not debug and not reload,
|
cache_headers=not debug and not reload,
|
||||||
cors=cors,
|
cors=cors,
|
||||||
|
|
|
||||||
|
|
@ -588,6 +588,27 @@ def to_css_class(s):
|
||||||
return "-".join(bits)
|
return "-".join(bits)
|
||||||
|
|
||||||
|
|
||||||
|
SQLITE_MAGIC = b"SQLite format 3\x00"
|
||||||
|
|
||||||
|
|
||||||
|
def is_valid_sqlite(path):
|
||||||
|
if not path.is_file():
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
with open(path, "rb") as fp:
|
||||||
|
has_magic = fp.read(len(SQLITE_MAGIC)) == SQLITE_MAGIC
|
||||||
|
except PermissionError:
|
||||||
|
return False
|
||||||
|
if not has_magic:
|
||||||
|
return False
|
||||||
|
# Check we can run `select * from sqlite_master`
|
||||||
|
try:
|
||||||
|
sqlite3.connect(str(path)).execute("select * from sqlite_master")
|
||||||
|
except Exception:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def link_or_copy(src, dst):
|
def link_or_copy(src, dst):
|
||||||
# Intended for use in populating a temp directory. We link if possible,
|
# Intended for use in populating a temp directory. We link if possible,
|
||||||
# but fall back to copying if the temp directory is on a different device
|
# but fall back to copying if the temp directory is on a different device
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ Usage: datasette serve [OPTIONS] [FILES]...
|
||||||
Serve up specified SQLite database files with a web UI
|
Serve up specified SQLite database files with a web UI
|
||||||
|
|
||||||
Options:
|
Options:
|
||||||
|
-d, --dir PATH Directories to scan for SQLite files to serve
|
||||||
-i, --immutable PATH Database files to open in immutable mode
|
-i, --immutable PATH Database files to open in immutable mode
|
||||||
-h, --host TEXT Host for server. Defaults to 127.0.0.1 which means only
|
-h, --host TEXT Host for server. Defaults to 127.0.0.1 which means only
|
||||||
connections from the local machine will be allowed. Use
|
connections from the local machine will be allowed. Use
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue