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 ##########################################################################################
DEFAULT_DATADIR = '.\\_etiquette'
DEFAULT_DATADIR = '_etiquette'
DEFAULT_DBNAME = 'phototagger.db'
DEFAULT_CONFIGNAME = 'config.json'
DEFAULT_THUMBDIR = 'site_thumbnails'

View file

@ -192,6 +192,13 @@ class FeatureDisabled(EtiquetteException):
'''
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):
'''
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_')
data_directory = self.ephemeral_directory.name
else:
data_directory = constants.DEFAULT_DATADIR
data_directory = pathclass.cwd().with_child(constants.DEFAULT_DATADIR)
if isinstance(data_directory, str):
data_directory = helpers.remove_path_badchars(data_directory, allowed=':/\\')
@ -1880,6 +1880,34 @@ class PhotoDB(
self.sql_executescript(constants.DB_PRAGMAS)
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):
self.close()

View file

@ -6,6 +6,7 @@ import sys
from voussoirkit import betterhelp
from voussoirkit import interactive
from voussoirkit import pathclass
from voussoirkit import pipeable
from voussoirkit import spinal
from voussoirkit import stringtools
from voussoirkit import vlogging
@ -14,31 +15,19 @@ import etiquette
LOG_LEVEL = vlogging.NOTSET
class CantFindPhotoDB(Exception):
pass
photodbs = {}
def find_photodb():
path = pathclass.cwd()
cwd = pathclass.cwd()
try:
return photodbs[cwd]
except KeyError:
pass
while True:
try:
return photodbs[path]
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
# If this raises, main will catch it.
photodb = etiquette.photodb.PhotoDB.closest_photodb()
photodbs[cwd] = photodb
return photodb
# HELPERS ##########################################################################################
@ -780,13 +769,18 @@ def main(argv):
)
return args
return betterhelp.subparser_main(
primary_args,
parser,
main_docstring=DOCSTRING,
sub_docstrings=SUB_DOCSTRINGS,
args_postprocessor=pp,
)
try:
return betterhelp.subparser_main(
primary_args,
parser,
main_docstring=DOCSTRING,
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__':
raise SystemExit(main(sys.argv[1:]))

View file

@ -320,4 +320,4 @@ def send_file(filepath, override_mimetype=None):
def init_photodb(*args, **kwargs):
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
from voussoirkit import pathclass
from voussoirkit import pipeable
from voussoirkit import vlogging
import etiquette
import backend
site = backend.site
@ -31,7 +33,6 @@ LOG_LEVEL = vlogging.NOTSET
def etiquette_flask_launch(
*,
create,
localhost_only,
port,
use_https,
@ -55,7 +56,12 @@ def etiquette_flask_launch(
if localhost_only:
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()}'
if use_https:
@ -69,7 +75,6 @@ def etiquette_flask_launch(
def etiquette_flask_launch_argparse(args):
return etiquette_flask_launch(
create=args.create,
localhost_only=args.localhost_only,
port=args.port,
use_https=args.use_https,
@ -82,7 +87,6 @@ def main(argv):
parser = argparse.ArgumentParser()
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('--localhost_only', '--localhost-only', dest='localhost_only', action='store_true')
parser.set_defaults(func=etiquette_flask_launch_argparse)

View file

@ -10,6 +10,7 @@ import sys
import traceback
from voussoirkit import interactive
from voussoirkit import pipeable
from voussoirkit import vlogging
import etiquette
@ -38,7 +39,13 @@ def photag(photo_id):
def erepl_argparse(args):
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:
exec(args.exec_statement)
@ -62,7 +69,6 @@ def main(argv):
parser = argparse.ArgumentParser()
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)
args = parser.parse_args(argv)