Add PhotoDB.closest_photodb for frontend instantiation.
This commit is contained in:
		
							parent
							
								
									84bf9b90de
								
							
						
					
					
						commit
						e5be26f8b0
					
				
					 7 changed files with 75 additions and 36 deletions
				
			
		|  | @ -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' | ||||||
|  |  | ||||||
|  | @ -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 | ||||||
|  |  | ||||||
|  | @ -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() | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -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:])) | ||||||
|  |  | ||||||
|  | @ -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) | ||||||
|  |  | ||||||
|  | @ -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) | ||||||
|  |  | ||||||
|  | @ -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) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in a new issue