diff --git a/docs/conf.py b/docs/conf.py index 5c425b9..8a37a98 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -2,6 +2,7 @@ import os import sys import alabaster + from sigal import __version__ # If extensions (or modules to document with autodoc) are in another directory, diff --git a/pyproject.toml b/pyproject.toml index 3afe0d8..964e08f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,7 +41,7 @@ tests = ["pytest", "pytest-cov"] docs = ["Sphinx>=4.1.0", "alabaster", "cryptography"] [project.scripts] -sigal = "sigal:main" +sigal = "sigal.__main__:main" [project.urls] repository = "https://github.com/saimn/sigal" diff --git a/src/sigal/__init__.py b/src/sigal/__init__.py index 1cdef94..b300acc 100644 --- a/src/sigal/__init__.py +++ b/src/sigal/__init__.py @@ -18,23 +18,6 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. -import importlib -import locale -import logging -import os -import pathlib -import socketserver -import sys -import time -from http import server - -import click -from click import argument, option - -from .gallery import Gallery -from .log import init_logging -from .settings import read_settings -from .utils import copy try: from .version import __version__ @@ -43,267 +26,3 @@ except ImportError: __version__ = None __url__ = "https://github.com/saimn/sigal" - -_DEFAULT_CONFIG_FILE = "sigal.conf.py" - - -@click.group() -@click.version_option(version=__version__) -def main(): - """Sigal - Simple Static Gallery Generator. - - Sigal is yet another python script to prepare a static gallery of images: - resize images, create thumbnails with some options, generate html pages. - - """ - - -@main.command() -@argument("path", default=_DEFAULT_CONFIG_FILE) -def init(path): - """Copy a sample config file in the current directory (default to - 'sigal.conf.py'), or use the provided 'path'.""" - - path = pathlib.Path(path) - if path.exists(): - print("Found an existing config file, will abort to keep it safe.") - sys.exit(1) - - conf = pathlib.Path(__file__).parent / "templates" / "sigal.conf.py" - path.write_text(conf.read_text()) - print(f"Sample config file created: {path}") - - -@main.command() -@argument("source", required=False) -@argument("destination", required=False) -@option("-f", "--force", is_flag=True, help="Force the reprocessing of existing images") -@option("-a", "--force-album", multiple=True, help="Force reprocessing of any album that matches the given pattern. Patterns containing no wildcards will be matched against only the album name. (-a 'My Pictures/* Pics' -a 'Festival')") -@option("-v", "--verbose", is_flag=True, help="Show all messages") -@option( - "-d", - "--debug", - is_flag=True, - help=( - "Show all messages, including debug messages. Also raise " - "exception if an error happen when processing files." - ), -) -@option("-q", "--quiet", is_flag=True, help="Show only error messages") -@option( - "-c", - "--config", - default=_DEFAULT_CONFIG_FILE, - show_default=True, - help="Configuration file", -) -@option( - "-t", - "--theme", - help=( - "Specify a theme directory, or a theme name for the themes included with Sigal" - ), -) -@option("--title", help="Title of the gallery (overrides the title setting.") -@option("-n", "--ncpu", help="Number of cpu to use (default: all)") -def build( - source, destination, debug, verbose, quiet, force, force_album, config, theme, title, ncpu -): - """Run sigal to process a directory. - - If provided, 'source', 'destination' and 'theme' will override the - corresponding values from the settings file. - - """ - if sum([debug, verbose, quiet]) > 1: - sys.exit("Only one option of debug, verbose and quiet should be used") - - if debug: - level = logging.DEBUG - elif verbose: - level = logging.INFO - elif quiet: - level = logging.ERROR - else: - level = logging.WARNING - - init_logging(__name__, level=level) - logger = logging.getLogger(__name__) - - if not os.path.isfile(config): - logger.error("Settings file not found: %s", config) - sys.exit(1) - - start_time = time.time() - settings = read_settings(config) - - for key in ("source", "destination", "theme"): - arg = locals()[key] - if arg is not None: - settings[key] = os.path.abspath(arg) - logger.info("%12s : %s", key.capitalize(), settings[key]) - - if not settings["source"] or not os.path.isdir(settings["source"]): - logger.error("Input directory not found: %s", settings["source"]) - sys.exit(1) - - # on windows os.path.relpath raises a ValueError if the two paths are on - # different drives, in that case we just ignore the exception as the two - # paths are anyway not relative - relative_check = True - try: - relative_check = os.path.relpath( - settings["destination"], settings["source"] - ).startswith("..") - except ValueError: - pass - - if not relative_check: - logger.error("Output directory should be outside of the input directory.") - sys.exit(1) - - if title: - settings["title"] = title - - locale.setlocale(locale.LC_ALL, settings["locale"]) - init_plugins(settings) - - gal = Gallery(settings, ncpu=ncpu, quiet=quiet) - gal.build(force=force_album if len(force_album) else force) - - # copy extra files - for src, dst in settings["files_to_copy"]: - src = os.path.join(settings["source"], src) - dst = os.path.join(settings["destination"], dst) - logger.debug("Copy %s to %s", src, dst) - copy(src, dst, symlink=settings["orig_link"], rellink=settings["rel_link"]) - - stats = gal.stats - - def format_stats(_type): - opt = [ - "{} {}".format(stats[_type + "_" + subtype], subtype) - for subtype in ("skipped", "failed") - if stats[_type + "_" + subtype] > 0 - ] - opt = " ({})".format(", ".join(opt)) if opt else "" - return f"{stats[_type]} {_type}s{opt}" - - if not quiet: - stats_str = "" - types = sorted({t.rsplit("_", 1)[0] for t in stats}) - for t in types[:-1]: - stats_str += f"{format_stats(t)} and " - stats_str += f"{format_stats(types[-1])}" - end_time = time.time() - start_time - print(f"Done, processed {stats_str} in {end_time:.2f} seconds.") - - -def init_plugins(settings): - """Load plugins and call register().""" - - logger = logging.getLogger(__name__) - logger.debug("Plugin paths: %s", settings["plugin_paths"]) - - for path in settings["plugin_paths"]: - sys.path.insert(0, path) - - for plugin in settings["plugins"]: - try: - if isinstance(plugin, str): - mod = importlib.import_module(plugin) - mod.register(settings) - else: - plugin.register(settings) - logger.debug("Registered plugin %s", plugin) - except Exception as e: - logger.error("Failed to load plugin %s: %r", plugin, e) - - for path in settings["plugin_paths"]: - sys.path.remove(path) - - -@main.command() -@argument("destination", default="_build") -@option("-p", "--port", help="Port to use", default=8000) -@option( - "-c", - "--config", - default=_DEFAULT_CONFIG_FILE, - show_default=True, - help="Configuration file", -) -def serve(destination, port, config): - """Run a simple web server.""" - if os.path.exists(destination): - pass - elif os.path.exists(config): - settings = read_settings(config) - destination = settings.get("destination") - if not os.path.exists(destination): - sys.stderr.write( - f"The '{destination}' directory doesn't exist, maybe try building" - " first?\n" - ) - sys.exit(1) - else: - sys.stderr.write( - f"The {destination} directory doesn't exist " - f"and the config file ({config}) could not be read.\n" - ) - sys.exit(2) - - print(f"DESTINATION : {destination}") - os.chdir(destination) - Handler = server.SimpleHTTPRequestHandler - httpd = socketserver.TCPServer(("", port), Handler, False) - print(f" * Running on http://127.0.0.1:{port}/") - - try: - httpd.allow_reuse_address = True - httpd.server_bind() - httpd.server_activate() - httpd.serve_forever() - except KeyboardInterrupt: - print("\nAll done!") - - -@main.command() -@argument("target") -@argument("keys", nargs=-1) -@option( - "-o", "--overwrite", default=False, is_flag=True, help="Overwrite existing .md file" -) -def set_meta(target, keys, overwrite=False): - """Write metadata keys to .md file. - - TARGET can be a media file or an album directory. KEYS are key/value pairs. - - Ex, to set the title of test.jpg to "My test image": - - sigal set_meta test.jpg title "My test image" - """ - - if not os.path.exists(target): - sys.stderr.write(f"The target {target} does not exist.\n") - sys.exit(1) - if len(keys) < 2 or len(keys) % 2 > 0: - sys.stderr.write("Need an even number of arguments.\n") - sys.exit(1) - - if os.path.isdir(target): - descfile = os.path.join(target, "index.md") - else: - descfile = os.path.splitext(target)[0] + ".md" - if os.path.exists(descfile) and not overwrite: - sys.stderr.write( - f"Description file '{descfile}' already exists. " - "Use --overwrite to overwrite it.\n" - ) - sys.exit(2) - - with open(descfile, "w") as fp: - for i in range(len(keys) // 2): - k, v = keys[i * 2 : (i + 1) * 2] - fp.write(f"{k.capitalize()}: {v}\n") - print(f"{len(keys) // 2} metadata key(s) written to {descfile}") diff --git a/src/sigal/__main__.py b/src/sigal/__main__.py new file mode 100644 index 0000000..9643ac9 --- /dev/null +++ b/src/sigal/__main__.py @@ -0,0 +1,305 @@ +# Copyright (c) 2009-2023 - Simon Conseil + +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: + +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. + +import locale +import logging +import os +import pathlib +import socketserver +import sys +import time +from http import server + +import click +from click import argument, option + +from .gallery import Gallery +from .log import init_logging +from .settings import read_settings +from .utils import copy, init_plugins + +try: + from .version import __version__ +except ImportError: + # package is not installed + __version__ = None + +_DEFAULT_CONFIG_FILE = "sigal.conf.py" + + +@click.group() +@click.version_option(version=__version__) +def main(): + """Sigal - Simple Static Gallery Generator. + + Sigal is yet another python script to prepare a static gallery of images: + resize images, create thumbnails with some options, generate html pages. + + """ + + +@main.command() +@argument("path", default=_DEFAULT_CONFIG_FILE) +def init(path): + """Copy a sample config file in the current directory (default to + 'sigal.conf.py'), or use the provided 'path'.""" + + path = pathlib.Path(path) + if path.exists(): + print("Found an existing config file, will abort to keep it safe.") + sys.exit(1) + + conf = pathlib.Path(__file__).parent / "templates" / "sigal.conf.py" + path.write_text(conf.read_text()) + print(f"Sample config file created: {path}") + + +@main.command() +@argument("source", required=False) +@argument("destination", required=False) +@option("-f", "--force", is_flag=True, help="Force the reprocessing of existing images") +@option( + "-a", + "--force-album", + multiple=True, + help=( + "Force reprocessing of any album that matches the given pattern. " + "Patterns containing no wildcards will be matched against only " + "the album name. (-a 'My Pictures/* Pics' -a 'Festival')" + ), +) +@option("-v", "--verbose", is_flag=True, help="Show all messages") +@option( + "-d", + "--debug", + is_flag=True, + help=( + "Show all messages, including debug messages. Also raise " + "exception if an error happen when processing files." + ), +) +@option("-q", "--quiet", is_flag=True, help="Show only error messages") +@option( + "-c", + "--config", + default=_DEFAULT_CONFIG_FILE, + show_default=True, + help="Configuration file", +) +@option( + "-t", + "--theme", + help=( + "Specify a theme directory, or a theme name for the themes included with Sigal" + ), +) +@option("--title", help="Title of the gallery (overrides the title setting.") +@option("-n", "--ncpu", help="Number of cpu to use (default: all)") +def build( + source, + destination, + debug, + verbose, + quiet, + force, + force_album, + config, + theme, + title, + ncpu, +): + """Run sigal to process a directory. + + If provided, 'source', 'destination' and 'theme' will override the + corresponding values from the settings file. + + """ + if sum([debug, verbose, quiet]) > 1: + sys.exit("Only one option of debug, verbose and quiet should be used") + + if debug: + level = logging.DEBUG + elif verbose: + level = logging.INFO + elif quiet: + level = logging.ERROR + else: + level = logging.WARNING + + init_logging(__name__, level=level) + logger = logging.getLogger(__name__) + + if not os.path.isfile(config): + logger.error("Settings file not found: %s", config) + sys.exit(1) + + start_time = time.time() + settings = read_settings(config) + + for key in ("source", "destination", "theme"): + arg = locals()[key] + if arg is not None: + settings[key] = os.path.abspath(arg) + logger.info("%12s : %s", key.capitalize(), settings[key]) + + if not settings["source"] or not os.path.isdir(settings["source"]): + logger.error("Input directory not found: %s", settings["source"]) + sys.exit(1) + + # on windows os.path.relpath raises a ValueError if the two paths are on + # different drives, in that case we just ignore the exception as the two + # paths are anyway not relative + relative_check = True + try: + relative_check = os.path.relpath( + settings["destination"], settings["source"] + ).startswith("..") + except ValueError: + pass + + if not relative_check: + logger.error("Output directory should be outside of the input directory.") + sys.exit(1) + + if title: + settings["title"] = title + + locale.setlocale(locale.LC_ALL, settings["locale"]) + init_plugins(settings) + + gal = Gallery(settings, ncpu=ncpu, quiet=quiet) + gal.build(force=force_album if len(force_album) else force) + + # copy extra files + for src, dst in settings["files_to_copy"]: + src = os.path.join(settings["source"], src) + dst = os.path.join(settings["destination"], dst) + logger.debug("Copy %s to %s", src, dst) + copy(src, dst, symlink=settings["orig_link"], rellink=settings["rel_link"]) + + stats = gal.stats + + def format_stats(_type): + opt = [ + "{} {}".format(stats[_type + "_" + subtype], subtype) + for subtype in ("skipped", "failed") + if stats[_type + "_" + subtype] > 0 + ] + opt = " ({})".format(", ".join(opt)) if opt else "" + return f"{stats[_type]} {_type}s{opt}" + + if not quiet: + stats_str = "" + types = sorted({t.rsplit("_", 1)[0] for t in stats}) + for t in types[:-1]: + stats_str += f"{format_stats(t)} and " + stats_str += f"{format_stats(types[-1])}" + end_time = time.time() - start_time + print(f"Done, processed {stats_str} in {end_time:.2f} seconds.") + + +@main.command() +@argument("destination", default="_build") +@option("-p", "--port", help="Port to use", default=8000) +@option( + "-c", + "--config", + default=_DEFAULT_CONFIG_FILE, + show_default=True, + help="Configuration file", +) +def serve(destination, port, config): + """Run a simple web server.""" + if os.path.exists(destination): + pass + elif os.path.exists(config): + settings = read_settings(config) + destination = settings.get("destination") + if not os.path.exists(destination): + sys.stderr.write( + f"The '{destination}' directory doesn't exist, maybe try building" + " first?\n" + ) + sys.exit(1) + else: + sys.stderr.write( + f"The {destination} directory doesn't exist " + f"and the config file ({config}) could not be read.\n" + ) + sys.exit(2) + + print(f"DESTINATION : {destination}") + os.chdir(destination) + Handler = server.SimpleHTTPRequestHandler + httpd = socketserver.TCPServer(("", port), Handler, False) + print(f" * Running on http://127.0.0.1:{port}/") + + try: + httpd.allow_reuse_address = True + httpd.server_bind() + httpd.server_activate() + httpd.serve_forever() + except KeyboardInterrupt: + print("\nAll done!") + + +@main.command() +@argument("target") +@argument("keys", nargs=-1) +@option( + "-o", "--overwrite", default=False, is_flag=True, help="Overwrite existing .md file" +) +def set_meta(target, keys, overwrite=False): + """Write metadata keys to .md file. + + TARGET can be a media file or an album directory. KEYS are key/value pairs. + + Ex, to set the title of test.jpg to "My test image": + + sigal set_meta test.jpg title "My test image" + """ + + if not os.path.exists(target): + sys.stderr.write(f"The target {target} does not exist.\n") + sys.exit(1) + if len(keys) < 2 or len(keys) % 2 > 0: + sys.stderr.write("Need an even number of arguments.\n") + sys.exit(1) + + if os.path.isdir(target): + descfile = os.path.join(target, "index.md") + else: + descfile = os.path.splitext(target)[0] + ".md" + if os.path.exists(descfile) and not overwrite: + sys.stderr.write( + f"Description file '{descfile}' already exists. " + "Use --overwrite to overwrite it.\n" + ) + sys.exit(2) + + with open(descfile, "w") as fp: + for i in range(len(keys) // 2): + k, v = keys[i * 2 : (i + 1) * 2] + fp.write(f"{k.capitalize()}: {v}\n") + print(f"{len(keys) // 2} metadata key(s) written to {descfile}") + + +if __name__ == "__main__": + main() diff --git a/src/sigal/gallery.py b/src/sigal/gallery.py index d449daf..7d13e9f 100644 --- a/src/sigal/gallery.py +++ b/src/sigal/gallery.py @@ -25,6 +25,7 @@ # IN THE SOFTWARE. import fnmatch +import io import logging import multiprocessing import os @@ -54,8 +55,8 @@ from .utils import ( get_mod_date, is_valid_html5_video, read_markdown, - url_from_path, should_reprocess_album, + url_from_path, ) from .video import process_video from .writer import AlbumListPageWriter, AlbumPageWriter @@ -718,10 +719,13 @@ class Gallery: ignore_files = settings["ignore_files"] progressChars = cycle(["/", "-", "\\", "|"]) + try: + isatty = os.isatty(sys.stdout.fileno()) + except io.UnsupportedOperation: + isatty = False + show_progress = ( - not quiet - and self.logger.getEffectiveLevel() >= logging.WARNING - and os.isatty(sys.stdout.fileno()) + not quiet and self.logger.getEffectiveLevel() >= logging.WARNING and isatty ) self.progressbar_target = None if show_progress else Devnull() @@ -937,7 +941,9 @@ class Gallery: def process_dir(self, album, force=False): """Process a list of images in a directory.""" for f in album: - if isfile(f.dst_path) and not should_reprocess_album(album.path, album.name, force): + if isfile(f.dst_path) and not should_reprocess_album( + album.path, album.name, force + ): self.logger.info("%s exists - skipping", f.dst_filename) self.stats[f.type + "_skipped"] += 1 else: diff --git a/src/sigal/utils.py b/src/sigal/utils.py index 0d41388..dc95f7b 100644 --- a/src/sigal/utils.py +++ b/src/sigal/utils.py @@ -18,12 +18,14 @@ # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS # IN THE SOFTWARE. +import importlib import logging import os import shutil +import sys +from fnmatch import fnmatch from functools import lru_cache from urllib.parse import quote -from fnmatch import fnmatch from markdown import Markdown from markupsafe import Markup @@ -82,18 +84,20 @@ def url_from_path(path): path = "/".join(path.split(os.sep)) return quote(path) + def should_reprocess_album(path, name, force=False): if isinstance(force, bool): return force else: for f in force: - if '*' in f or '?' in f: + if "*" in f or "?" in f: if fnmatch(path, f): return True elif name == f: return True return False + def read_markdown(filename): """Reads markdown file, converts output and fetches title and meta-data for further processing. @@ -147,6 +151,30 @@ def get_mime(ext): return VIDEO_MIMES[ext] +def init_plugins(settings): + """Load plugins and call register().""" + + logger = logging.getLogger(__name__) + logger.debug("Plugin paths: %s", settings["plugin_paths"]) + + for path in settings["plugin_paths"]: + sys.path.insert(0, path) + + for plugin in settings["plugins"]: + try: + if isinstance(plugin, str): + mod = importlib.import_module(plugin) + mod.register(settings) + else: + plugin.register(settings) + logger.debug("Registered plugin %s", plugin) + except Exception as e: + logger.error("Failed to load plugin %s: %r", plugin, e) + + for path in settings["plugin_paths"]: + sys.path.remove(path) + + class raise_if_debug: def __init__(self): self.value = None diff --git a/tests/test_cli.py b/tests/test_cli.py index 601129f..4c9c9b3 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -4,7 +4,7 @@ from os.path import join from click.testing import CliRunner -from sigal import build, init, serve, set_meta +from sigal.__main__ import build, init, serve, set_meta TESTGAL = join(os.path.abspath(os.path.dirname(__file__)), "sample") @@ -41,15 +41,20 @@ def test_build(tmpdir, disconnect_signals): ) result = runner.invoke(build, ["-n", 1, "--debug"]) + assert result.output == "Settings file not found: sigal.conf.py\n" assert result.exit_code == 1 os.chdir(tmpdir) result = runner.invoke(build, ["foo", "-n", 1, "--debug"]) assert result.exit_code == 1 + assert "Input directory not found" in result.output result = runner.invoke(build, ["pictures", "pictures/out", "-n", 1, "--debug"]) assert result.exit_code == 1 + assert ( + "Output directory should be outside of the input directory" in result.output + ) with open(config_file) as f: text = f.read() @@ -59,7 +64,7 @@ theme = 'colorbox' files_to_copy = (('../watermark.png', 'watermark.png'),) plugins = ['sigal.plugins.adjust', 'sigal.plugins.copyright', 'sigal.plugins.watermark', 'sigal.plugins.feeds', - 'sigal.plugins.media_page' 'sigal.plugins.nomedia', + 'sigal.plugins.media_page', 'sigal.plugins.nomedia', 'sigal.plugins.extended_caching'] copyright = "An example copyright message" copyright_text_font = "foobar" @@ -74,7 +79,9 @@ atom_feed = {'feed_url': 'http://example.org/feed.atom', 'nb_items': 10} f.write(text) result = runner.invoke( - build, ["pictures", "build", "--title", "Testing build", "-n", 1, "--debug"] + build, + ["pictures", "build", "--title", "Testing build", "-n", 1, "--debug"], + catch_exceptions=False, ) assert result.exit_code == 0 assert os.path.isfile( diff --git a/tests/test_compress_assets_plugin.py b/tests/test_compress_assets_plugin.py index 889e78b..9000363 100644 --- a/tests/test_compress_assets_plugin.py +++ b/tests/test_compress_assets_plugin.py @@ -4,9 +4,9 @@ from unittest import mock import pytest -from sigal import init_plugins from sigal.gallery import Gallery from sigal.plugins import compress_assets +from sigal.utils import init_plugins CURRENT_DIR = os.path.dirname(__file__) diff --git a/tests/test_encrypt.py b/tests/test_encrypt.py index a305f85..5179359 100644 --- a/tests/test_encrypt.py +++ b/tests/test_encrypt.py @@ -4,10 +4,10 @@ from io import BytesIO import pytest -from sigal import init_plugins from sigal.gallery import Gallery from sigal.plugins.encrypt import endec from sigal.plugins.encrypt.encrypt import cache_key +from sigal.utils import init_plugins CURRENT_DIR = os.path.dirname(__file__) diff --git a/tests/test_image.py b/tests/test_image.py index 4bc032c..449c641 100644 --- a/tests/test_image.py +++ b/tests/test_image.py @@ -4,7 +4,6 @@ from unittest.mock import patch import pytest from PIL import Image as PILImage -from sigal import init_logging from sigal.gallery import Image from sigal.image import ( generate_image, @@ -16,6 +15,7 @@ from sigal.image import ( get_size, process_image, ) +from sigal.log import init_logging from sigal.settings import Status, create_settings CURRENT_DIR = os.path.dirname(__file__) diff --git a/tests/test_plugins.py b/tests/test_plugins.py index 1e464c2..4dda019 100644 --- a/tests/test_plugins.py +++ b/tests/test_plugins.py @@ -1,7 +1,7 @@ import os -from sigal import init_plugins from sigal.gallery import Gallery +from sigal.utils import init_plugins CURRENT_DIR = os.path.dirname(__file__) diff --git a/tests/test_utils.py b/tests/test_utils.py index c937304..b404dd6 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -43,16 +43,43 @@ def test_copy(tmpdir): utils.copy(src, dst) utils.copy(src, dst) + def test_force(tmpdir): - assert utils.should_reprocess_album('Gallery/New Pics', 'New Pics', False) is False - assert utils.should_reprocess_album('Gallery/New Pics', 'New Pics', True) is True - assert utils.should_reprocess_album('Gallery/New Pics', 'New Pics', ['Gallery/*']) is True - assert utils.should_reprocess_album('Gallery/New Pics', 'New Pics', ['Gallery/*Pics']) is True - assert utils.should_reprocess_album('Gallery/New Pics', 'New Pics', ['Pictures/*']) is False - assert utils.should_reprocess_album('Gallery/New Pics', 'New Pics', ['New Pics']) is True - assert utils.should_reprocess_album('Gallery/New Pics', 'New Pics', ['Pictures']) is False - assert utils.should_reprocess_album('Gallery/New Pics', 'New Pics', ['Pictures', 'Something']) is False - assert utils.should_reprocess_album('Gallery/New Pics', 'New Pics', ['Pictures', 'Gallery', '*Pics']) is True + assert utils.should_reprocess_album("Gallery/New Pics", "New Pics", False) is False + assert utils.should_reprocess_album("Gallery/New Pics", "New Pics", True) is True + assert ( + utils.should_reprocess_album("Gallery/New Pics", "New Pics", ["Gallery/*"]) + is True + ) + assert ( + utils.should_reprocess_album("Gallery/New Pics", "New Pics", ["Gallery/*Pics"]) + is True + ) + assert ( + utils.should_reprocess_album("Gallery/New Pics", "New Pics", ["Pictures/*"]) + is False + ) + assert ( + utils.should_reprocess_album("Gallery/New Pics", "New Pics", ["New Pics"]) + is True + ) + assert ( + utils.should_reprocess_album("Gallery/New Pics", "New Pics", ["Pictures"]) + is False + ) + assert ( + utils.should_reprocess_album( + "Gallery/New Pics", "New Pics", ["Pictures", "Something"] + ) + is False + ) + assert ( + utils.should_reprocess_album( + "Gallery/New Pics", "New Pics", ["Pictures", "Gallery", "*Pics"] + ) + is True + ) + def test_check_or_create_dir(tmpdir): path = str(tmpdir.join("new_directory")) diff --git a/tests/test_zip.py b/tests/test_zip.py index b46ad48..a5450cc 100644 --- a/tests/test_zip.py +++ b/tests/test_zip.py @@ -1,9 +1,9 @@ import os import zipfile -from sigal import init_plugins from sigal.gallery import Gallery from sigal.settings import read_settings +from sigal.utils import init_plugins CURRENT_DIR = os.path.dirname(__file__) SAMPLE_DIR = os.path.join(CURRENT_DIR, "sample")