diff --git a/AUTHORS b/AUTHORS index 14a185f5..60b9c420 100644 --- a/AUTHORS +++ b/AUTHORS @@ -3,4 +3,5 @@ alphabetical order): - Christophe-Marie Duquesne - Matthias Vogelgesang +- Vikram Shirgur - Yuce Tekol diff --git a/docs/changelog.rst b/docs/changelog.rst index 6c8d3234..6f396d23 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -2,6 +2,11 @@ Changelog =========== +Version 0.9.dev +~~~~~~~~~~~~~~~ + +Released on 2014-xx-xx. + Version 0.8.0 ~~~~~~~~~~~~~ diff --git a/docs/getting_started.rst b/docs/getting_started.rst index 0c655564..83d51c40 100644 --- a/docs/getting_started.rst +++ b/docs/getting_started.rst @@ -34,7 +34,7 @@ Serve ``index.html`` to the urls to allow browsing without a server. -Help of the ``sigal build`` command +Help on the ``sigal build`` command ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ :: @@ -73,3 +73,22 @@ Optional arguments: ``-n NCPU, --ncpu NCPU`` Number of cpu to use (default: all) + +Help on the ``sigal serve`` command +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + $ sigal serve [-c CONFIG] [-p PORT] [destination] + +Optional arguments: + +``-c CONFIG, --config CONFIG`` + Configuration file (default: ``sigal.conf.py`` in the current working + directory) + +``-p PORT, --port PORT`` + Port number to start the server on (default: 8000) + +``destination`` + Destination directory where the output of build is located (default: _build) diff --git a/docs/plugins.rst b/docs/plugins.rst index 95b7c128..06d6b2bf 100644 --- a/docs/plugins.rst +++ b/docs/plugins.rst @@ -7,15 +7,24 @@ How to use plugins Plugins must be specified with the ``plugins`` setting: +.. code-block:: python + + plugins = ['sigal.plugins.adjust', 'sigal.plugins.copyright'] + +You can either specify the name of the module which contains the plugin, or +import the plugin before adding it to the list: + .. code-block:: python from sigal.plugins import copyright plugins = ['sigal.plugins.adjust', copyright] -You can either specify the name of the module which contains the plugin, or -import the plugin before adding it to the list. The ``plugin_paths`` setting -can be used to specify paths to search for plugins (if they are not in the -python path). +.. note:: Using an import like this will break the multiprocessing feature, + because the settings dict must be serializable. So in most cases you should + prefer the first option. + +The ``plugin_paths`` setting can be used to specify paths to search for plugins +(if they are not in the python path). Write a new plugin ------------------ diff --git a/sigal/__init__.py b/sigal/__init__.py index b9e8e5fe..61bdaa68 100644 --- a/sigal/__init__.py +++ b/sigal/__init__.py @@ -169,24 +169,40 @@ def init_plugins(settings): @main.command() -@argument('path', default='_build') +@argument('destination', default='_build') @option('-p', '--port', help="Port to use", default=8000) -def serve(path, port): +@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(path): - os.chdir(path) - Handler = server.SimpleHTTPRequestHandler - httpd = socketserver.TCPServer(("", port), Handler, False) - print(" * Running on http://127.0.0.1:{}/".format(port)) - - try: - httpd.allow_reuse_address = True - httpd.server_bind() - httpd.server_activate() - httpd.serve_forever() - except KeyboardInterrupt: - print('\nAll done!') + 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("The '{}' directory doesn't exist, " + "maybe try building first?" + "\n".format(destination)) + sys.exit(1) else: - sys.stderr.write("The '%s' directory doesn't exist.\n" % path) - sys.exit(1) + sys.stderr.write("The {destination} directory doesn't exist " + "and the config file ({config}) could not be " + "read." + "\n".format(destination=destination, config=config)) + sys.exit(2) + + print('DESTINATION : {}'.format(destination)) + os.chdir(destination) + Handler = server.SimpleHTTPRequestHandler + httpd = socketserver.TCPServer(("", port), Handler, False) + print(" * Running on http://127.0.0.1:{}/".format(port)) + + try: + httpd.allow_reuse_address = True + httpd.server_bind() + httpd.server_activate() + httpd.serve_forever() + except KeyboardInterrupt: + print('\nAll done!') + diff --git a/sigal/gallery.py b/sigal/gallery.py index af0d2221..22b29781 100644 --- a/sigal/gallery.py +++ b/sigal/gallery.py @@ -27,6 +27,7 @@ import logging import multiprocessing import os +import pickle import sys import zipfile @@ -97,10 +98,15 @@ def __unicode__(self): @property def big(self): """Path to the original image, if ``keep_orig`` is set (relative to the - album directory). + album directory). Copy the file if needed. """ if self.settings['keep_orig']: - return os.path.join(self.settings['orig_dir'], self.src_filename) + s = self.settings + orig_path = join(s['destination'], self.path, s['orig_dir']) + check_or_create_dir(orig_path) + copy(self.src_path, join(orig_path, self.src_filename), + symlink=s['orig_link']) + return url_from_path(join(s['orig_dir'], self.src_filename)) else: return None @@ -221,7 +227,6 @@ def __init__(self, path, settings, dirnames, filenames, gallery): self.name = path.split(os.path.sep)[-1] self.gallery = gallery self.settings = settings - self.orig_path = None self._thumbnail = None if path == '.': @@ -604,6 +609,13 @@ def log_func(x): except KeyboardInterrupt: self.pool.terminate() sys.exit('Interrupted') + except pickle.PicklingError: + self.logger.critical( + "Failed to process files with the multiprocessing feature." + " This can be caused by some module import or object " + "defined in the settings file, which can't be serialized.", + exc_info=True) + sys.exit('Abort') print('') else: @@ -621,16 +633,11 @@ def log_func(x): 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 force: self.logger.info("%s exists - skipping", f.filename) self.stats[f.type + '_skipped'] += 1 else: - if self.settings['keep_orig']: - copy(f.src_path, join(album.orig_path, f.filename), - symlink=self.settings['orig_link']) - self.stats[f.type] += 1 yield f.type, f.src_path, album.dst_path, self.settings diff --git a/sigal/pkgmeta.py b/sigal/pkgmeta.py index cce04084..657745d5 100644 --- a/sigal/pkgmeta.py +++ b/sigal/pkgmeta.py @@ -22,7 +22,7 @@ __title__ = 'sigal' __author__ = 'Simon Conseil' -__version__ = '0.8.0' +__version__ = '0.9.0-dev' __license__ = 'MIT' __url__ = 'https://github.com/saimn/sigal' __all__ = ['__title__', '__author__', '__version__', '__license__', '__url__'] diff --git a/sigal/templates/sigal.conf.py b/sigal/templates/sigal.conf.py index 26dd48c0..0d27dff8 100644 --- a/sigal/templates/sigal.conf.py +++ b/sigal/templates/sigal.conf.py @@ -138,6 +138,12 @@ # Plugins # -------- +# List of plugins to use. The values must be a path than can be imported. +# Another option is to import the plugin and put the module in the list, but +# this will break with the multiprocessing feature (the settings dict obtained +# from this file must be serializable). +# plugins = ['sigal.plugins.adjust', 'sigal.plugins.copyright'] + # Add a copyright text on the image (default: '') # copyright = "© An example copyright message" diff --git a/sigal/video.py b/sigal/video.py index 65ecd4e8..166f8b8e 100644 --- a/sigal/video.py +++ b/sigal/video.py @@ -44,7 +44,8 @@ def check_subprocess(cmd, source, outname): returncode, stdout, stderr = call_subprocess(cmd) except KeyboardInterrupt: logger.debug('Process terminated, removing file %s', outname) - os.remove(outname) + if os.path.isfile(outname): + os.remove(outname) raise if returncode: @@ -52,7 +53,8 @@ def check_subprocess(cmd, source, outname): logger.debug('STDOUT:\n %s', stdout) logger.debug('STDERR:\n %s', stderr) logger.debug('Process failed, removing file %s', outname) - os.remove(outname) + if os.path.isfile(outname): + os.remove(outname) def video_size(source): diff --git a/tests/sample/sigal.conf.py b/tests/sample/sigal.conf.py index ec6a44a0..e693f83e 100644 --- a/tests/sample/sigal.conf.py +++ b/tests/sample/sigal.conf.py @@ -8,8 +8,7 @@ links = [('Example link', 'http://example.org'), ('Another link', 'http://example.org')] -from sigal.plugins import copyright -plugins = ['sigal.plugins.adjust', copyright] +plugins = ['sigal.plugins.adjust', 'sigal.plugins.copyright'] copyright = u"© An example copyright message" adjust_options = {'color': 0.0, 'brightness': 1.0, 'contrast': 1.0, 'sharpness': 0.0} diff --git a/tests/test_cli.py b/tests/test_cli.py index a6862804..86f0912f 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -4,6 +4,7 @@ from click.testing import CliRunner from sigal import init +from sigal import serve def test_init(tmpdir): @@ -18,3 +19,16 @@ def test_init(tmpdir): assert result.exit_code == 1 assert result.output == ("Found an existing config file, will abort to " "keep it safe.\n") + +def test_serve(tmpdir): + config_file = str(tmpdir.join('sigal.conf.py')) + runner = CliRunner() + result = runner.invoke(init, [config_file]) + assert result.exit_code == 0 + + result = runner.invoke(serve) + assert result.exit_code == 2 + + result = runner.invoke(serve, ['-c', config_file]) + assert result.exit_code == 1 + diff --git a/tests/test_gallery.py b/tests/test_gallery.py index f1f9034a..1bd86ec5 100644 --- a/tests/test_gallery.py +++ b/tests/test_gallery.py @@ -79,18 +79,21 @@ def test_media(settings): assert str(m) == file_path -def test_media_orig(settings): +def test_media_orig(settings, tmpdir): settings['keep_orig'] = False m = Media('11.jpg', 'dir1/test1', settings) assert m.big is None settings['keep_orig'] = True + settings['destination'] = str(tmpdir) + m = Image('11.jpg', 'dir1/test1', settings) assert m.big == 'original/11.jpg' - m = Video('file.ogv', 'video', settings) - assert m.filename == 'file.webm' - assert m.big == 'original/file.ogv' + m = Video('stallman software-freedom-day-low.ogv', 'video', settings) + assert m.filename == 'stallman software-freedom-day-low.webm' + assert m.big == 'original/stallman software-freedom-day-low.ogv' + assert os.path.isfile(join(settings['destination'], m.path, m.big)) def test_image(settings, tmpdir):