Add PhotoDB.closest_photodb for frontend instantiation.

This commit is contained in:
voussoir 2021-01-09 15:41:52 -08:00
parent 84bf9b90de
commit e5be26f8b0
7 changed files with 75 additions and 36 deletions

View file

@ -252,7 +252,7 @@ ADDITIONAL_MIMETYPES = {
# Photodb ########################################################################################## # Photodb ##########################################################################################
DEFAULT_DATADIR = '.\\_etiquette' DEFAULT_DATADIR = '_etiquette'
DEFAULT_DBNAME = 'phototagger.db' DEFAULT_DBNAME = 'phototagger.db'
DEFAULT_CONFIGNAME = 'config.json' DEFAULT_CONFIGNAME = 'config.json'
DEFAULT_THUMBDIR = 'site_thumbnails' DEFAULT_THUMBDIR = 'site_thumbnails'

View file

@ -192,6 +192,13 @@ class FeatureDisabled(EtiquetteException):
''' '''
error_message = 'This feature has been disabled. Requires {}.' error_message = 'This feature has been disabled. Requires {}.'
class NoClosestPhotoDB(EtiquetteException):
'''
For calls to PhotoDB.closest_photodb where none exists between cwd and
drive root.
'''
error_message = 'There is no PhotoDB in {} or its parents.'
class NoYields(EtiquetteException): class NoYields(EtiquetteException):
''' '''
For when all of the yield_* arguments have been provided as False, and thus For when all of the yield_* arguments have been provided as False, and thus

View file

@ -1805,7 +1805,7 @@ class PhotoDB(
self.ephemeral_directory = tempfile.TemporaryDirectory(prefix='etiquette_ephem_') self.ephemeral_directory = tempfile.TemporaryDirectory(prefix='etiquette_ephem_')
data_directory = self.ephemeral_directory.name data_directory = self.ephemeral_directory.name
else: else:
data_directory = constants.DEFAULT_DATADIR data_directory = pathclass.cwd().with_child(constants.DEFAULT_DATADIR)
if isinstance(data_directory, str): if isinstance(data_directory, str):
data_directory = helpers.remove_path_badchars(data_directory, allowed=':/\\') data_directory = helpers.remove_path_badchars(data_directory, allowed=':/\\')
@ -1880,6 +1880,34 @@ class PhotoDB(
self.sql_executescript(constants.DB_PRAGMAS) self.sql_executescript(constants.DB_PRAGMAS)
self.sql.commit() self.sql.commit()
@classmethod
def closest_photodb(cls, *args, **kwargs):
'''
Starting from the cwd and climbing upwards towards the filesystem root,
look for an existing Etiquette data directory and return the PhotoDB
object. If none exists, raise exceptions.NoClosestPhotoDB.
'''
cwd = pathclass.cwd()
path = cwd
while True:
if path.with_child(constants.DEFAULT_DATADIR).is_dir:
break
parent = path.parent
if path == parent:
raise exceptions.NoClosestPhotoDB(cwd)
path = parent
path = path.with_child(constants.DEFAULT_DATADIR)
photodb = cls(
path,
create=False,
*args,
**kwargs,
)
photodb.log.debug('Found closest PhotoDB at %s.', path)
return photodb
def __del__(self): def __del__(self):
self.close() self.close()

View file

@ -6,6 +6,7 @@ import sys
from voussoirkit import betterhelp from voussoirkit import betterhelp
from voussoirkit import interactive from voussoirkit import interactive
from voussoirkit import pathclass from voussoirkit import pathclass
from voussoirkit import pipeable
from voussoirkit import spinal from voussoirkit import spinal
from voussoirkit import stringtools from voussoirkit import stringtools
from voussoirkit import vlogging from voussoirkit import vlogging
@ -14,31 +15,19 @@ import etiquette
LOG_LEVEL = vlogging.NOTSET LOG_LEVEL = vlogging.NOTSET
class CantFindPhotoDB(Exception):
pass
photodbs = {} photodbs = {}
def find_photodb(): def find_photodb():
path = pathclass.cwd() cwd = pathclass.cwd()
try:
return photodbs[cwd]
except KeyError:
pass
while True: # If this raises, main will catch it.
try: photodb = etiquette.photodb.PhotoDB.closest_photodb()
return photodbs[path] photodbs[cwd] = photodb
except KeyError:
pass
if path.with_child('_etiquette').is_dir:
break
if path == path.parent:
raise CantFindPhotoDB()
path = path.parent
photodb = etiquette.photodb.PhotoDB(
path.with_child('_etiquette'),
create=False,
log_level=LOG_LEVEL,
)
photodbs[path] = photodb
return photodb return photodb
# HELPERS ########################################################################################## # HELPERS ##########################################################################################
@ -780,13 +769,18 @@ def main(argv):
) )
return args return args
return betterhelp.subparser_main( try:
primary_args, return betterhelp.subparser_main(
parser, primary_args,
main_docstring=DOCSTRING, parser,
sub_docstrings=SUB_DOCSTRINGS, main_docstring=DOCSTRING,
args_postprocessor=pp, sub_docstrings=SUB_DOCSTRINGS,
) args_postprocessor=pp,
)
except etiquette.exceptions.NoClosestPhotoDB as exc:
pipeable.stderr(exc.error_message)
pipeable.stderr('Try etiquette_cli init')
return 1
if __name__ == '__main__': if __name__ == '__main__':
raise SystemExit(main(sys.argv[1:])) raise SystemExit(main(sys.argv[1:]))

View file

@ -320,4 +320,4 @@ def send_file(filepath, override_mimetype=None):
def init_photodb(*args, **kwargs): def init_photodb(*args, **kwargs):
global P global P
P = etiquette.photodb.PhotoDB(*args, **kwargs) P = etiquette.photodb.PhotoDB.closest_photodb(*args, **kwargs)

View file

@ -18,8 +18,10 @@ import os
import sys import sys
from voussoirkit import pathclass from voussoirkit import pathclass
from voussoirkit import pipeable
from voussoirkit import vlogging from voussoirkit import vlogging
import etiquette
import backend import backend
site = backend.site site = backend.site
@ -31,7 +33,6 @@ LOG_LEVEL = vlogging.NOTSET
def etiquette_flask_launch( def etiquette_flask_launch(
*, *,
create,
localhost_only, localhost_only,
port, port,
use_https, use_https,
@ -55,7 +56,12 @@ def etiquette_flask_launch(
if localhost_only: if localhost_only:
site.localhost_only = True site.localhost_only = True
backend.common.init_photodb(create=create, log_level=LOG_LEVEL) try:
backend.common.init_photodb(log_level=LOG_LEVEL)
except etiquette.exceptions.NoClosestPhotoDB as exc:
pipeable.stderr(exc.error_message)
pipeable.stderr('Try etiquette_cli init')
return 1
message = f'Starting server on port {port}, pid={os.getpid()}' message = f'Starting server on port {port}, pid={os.getpid()}'
if use_https: if use_https:
@ -69,7 +75,6 @@ def etiquette_flask_launch(
def etiquette_flask_launch_argparse(args): def etiquette_flask_launch_argparse(args):
return etiquette_flask_launch( return etiquette_flask_launch(
create=args.create,
localhost_only=args.localhost_only, localhost_only=args.localhost_only,
port=args.port, port=args.port,
use_https=args.use_https, use_https=args.use_https,
@ -82,7 +87,6 @@ def main(argv):
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('port', nargs='?', type=int, default=5000) parser.add_argument('port', nargs='?', type=int, default=5000)
parser.add_argument('--dont_create', '--dont-create', '--no-create', dest='create', action='store_false', default=True)
parser.add_argument('--https', dest='use_https', action='store_true', default=None) parser.add_argument('--https', dest='use_https', action='store_true', default=None)
parser.add_argument('--localhost_only', '--localhost-only', dest='localhost_only', action='store_true') parser.add_argument('--localhost_only', '--localhost-only', dest='localhost_only', action='store_true')
parser.set_defaults(func=etiquette_flask_launch_argparse) parser.set_defaults(func=etiquette_flask_launch_argparse)

View file

@ -10,6 +10,7 @@ import sys
import traceback import traceback
from voussoirkit import interactive from voussoirkit import interactive
from voussoirkit import pipeable
from voussoirkit import vlogging from voussoirkit import vlogging
import etiquette import etiquette
@ -38,7 +39,13 @@ def photag(photo_id):
def erepl_argparse(args): def erepl_argparse(args):
global P global P
P = etiquette.photodb.PhotoDB(create=args.create, log_level=LOG_LEVEL)
try:
P = etiquette.photodb.PhotoDB.closest_photodb(log_level=LOG_LEVEL)
except etiquette.exceptions.NoClosestPhotoDB as exc:
pipeable.stderr(exc.error_message)
pipeable.stderr('Try etiquette_cli init')
return 1
if args.exec_statement: if args.exec_statement:
exec(args.exec_statement) exec(args.exec_statement)
@ -62,7 +69,6 @@ def main(argv):
parser = argparse.ArgumentParser() parser = argparse.ArgumentParser()
parser.add_argument('--exec', dest='exec_statement', default=None) parser.add_argument('--exec', dest='exec_statement', default=None)
parser.add_argument('--dont_create', '--dont-create', '--no-create', dest='create', action='store_false', default=True)
parser.set_defaults(func=erepl_argparse) parser.set_defaults(func=erepl_argparse)
args = parser.parse_args(argv) args = parser.parse_args(argv)