From 21c4041f3be5118822f221e9381afc11d057d4a3 Mon Sep 17 00:00:00 2001 From: Simon Conseil Date: Sat, 28 Sep 2013 23:06:33 +0200 Subject: [PATCH 1/5] py3 compat: print, iteritems, imports --- setup.py | 10 +++++++--- sigal/__init__.py | 17 ++++++++--------- sigal/compat.py | 14 ++++++++++++++ sigal/gallery.py | 7 ++++++- sigal/pkgmeta.py | 6 +++--- sigal/settings.py | 9 +++++++-- 6 files changed, 45 insertions(+), 18 deletions(-) create mode 100644 sigal/compat.py diff --git a/setup.py b/setup.py index f591bba..2f2adb1 100644 --- a/setup.py +++ b/setup.py @@ -22,8 +22,10 @@ with open('docs/changelog.rst') as f: # Load package meta from the pkgmeta module without loading the package. pkgmeta = {} -execfile(os.path.join(os.path.dirname(__file__), 'sigal', 'pkgmeta.py'), - pkgmeta) +pkgmeta_file = os.path.join(os.path.dirname(__file__), 'sigal', 'pkgmeta.py') +with open(pkgmeta_file) as f: + code = compile(f.read(), 'pkgmeta.py', 'exec') + exec(code, pkgmeta) setup( name='sigal', @@ -46,8 +48,10 @@ setup( 'Environment :: Console', 'License :: OSI Approved :: MIT License', 'Operating System :: OS Independent', - 'Programming Language :: Python :: 2.6', + 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.3', 'Topic :: Internet :: WWW/HTTP', 'Topic :: Multimedia :: Graphics :: Viewers', 'Topic :: Software Development :: Libraries :: Python Modules', diff --git a/sigal/__init__.py b/sigal/__init__.py index c4e1ec4..52fb2c6 100644 --- a/sigal/__init__.py +++ b/sigal/__init__.py @@ -30,9 +30,9 @@ sigal is yet another python script to prepare a static gallery of images: * generate html pages. """ -from __future__ import absolute_import +from __future__ import absolute_import, print_function -import codecs +import io import logging import os import sys @@ -72,9 +72,9 @@ def init(): from pkg_resources import resource_string conf = resource_string(__name__, 'templates/sigal.conf.py') - with codecs.open('sigal.conf.py', 'w', 'utf-8') as f: + with io.open('sigal.conf.py', 'w', 'utf-8') as f: f.write(conf) - print "Sample config file created: sigal.conf.py" + print("Sample config file created: sigal.conf.py") @arg('source', nargs='?', help='Input directory') @@ -97,7 +97,7 @@ def build(source, destination, debug=False, verbose=False, force=False, settings_file = config or _DEFAULT_CONFIG_FILE if not os.path.isfile(settings_file): - logger.error("Settings file not found (%s)", settings_file) + logger.error("Settings file not found: %s", settings_file) sys.exit(1) settings = read_settings(settings_file) @@ -108,8 +108,7 @@ def build(source, destination, debug=False, verbose=False, force=False, logger.info("Input : %s", settings['source']) if not settings['source'] or not os.path.isdir(settings['source']): - logger.error("Input directory '%s' does not exist.", - settings['source']) + logger.error("Input directory not found: %s", settings['source']) sys.exit(1) logger.info("Output : %s", settings['destination']) @@ -138,7 +137,7 @@ def serve(path): Handler = SimpleHTTPServer.SimpleHTTPRequestHandler httpd = SocketServer.TCPServer(("", PORT), Handler, False) - print " * Running on http://127.0.0.1:%i/" % PORT + print(" * Running on http://127.0.0.1:{}/".format(PORT)) try: httpd.allow_reuse_address = True @@ -146,7 +145,7 @@ def serve(path): httpd.server_activate() httpd.serve_forever() except KeyboardInterrupt: - print '\nAll done!' + print('\nAll done!') else: sys.stderr.write("The '%s' directory doesn't exist.\n" % path) diff --git a/sigal/compat.py b/sigal/compat.py new file mode 100644 index 0000000..ad1002b --- /dev/null +++ b/sigal/compat.py @@ -0,0 +1,14 @@ +# -*- coding: utf-8 -*- + +import sys + +PY2 = sys.version_info[0] == 2 + +if not PY2: + text_type = str + string_types = (str,) + unichr = chr +else: + text_type = unicode # NOQA + string_types = (str, unicode) # NOQA + unichr = unichr diff --git a/sigal/gallery.py b/sigal/gallery.py index 2b4b9a7..c0e10c8 100644 --- a/sigal/gallery.py +++ b/sigal/gallery.py @@ -38,9 +38,13 @@ from PIL import Image as PILImage import sigal.image import sigal.video +from . import compat from .settings import get_thumb from .writer import Writer +if not compat.PY2: + from functools import reduce + DESCRIPTION_FILE = "index.md" # Label with for the progress bar. The max value is 48 character = 80 - 32 for @@ -96,7 +100,8 @@ class PathsDb(object): } # get information for each directory - for path, dirnames, filenames in os.walk(self.basepath, followlinks=True): + for path, dirnames, filenames in os.walk(self.basepath, + followlinks=True): relpath = os.path.relpath(path, self.basepath) # sort images and sub-albums by name diff --git a/sigal/pkgmeta.py b/sigal/pkgmeta.py index af3426a..0264f42 100644 --- a/sigal/pkgmeta.py +++ b/sigal/pkgmeta.py @@ -1,8 +1,8 @@ # -*- coding:utf-8 -*- __title__ = 'sigal' -__author__ = u"Simon Conseil" -__version__ = "0.5.1" -__license__ = "MIT" +__author__ = 'Simon Conseil' +__version__ = '0.5.1' +__license__ = 'MIT' __url__ = 'https://github.com/saimn/sigal' __all__ = ['__title__', '__author__', '__version__', '__license__', '__url__'] diff --git a/sigal/settings.py b/sigal/settings.py index 32a18b8..b3af3e2 100644 --- a/sigal/settings.py +++ b/sigal/settings.py @@ -60,6 +60,7 @@ def get_thumb(settings, filename): """Return the path to the thumb. examples: + >>> default_settings = create_settings() >>> get_thumb(default_settings, "bar/foo.jpg") "bar/thumbnails/foo.jpg" >>> get_thumb(default_settings, "bar/foo.png") @@ -96,8 +97,12 @@ def read_settings(filename=None): if filename: logger.debug("Settings file: %s", filename) tempdict = {} - execfile(filename, tempdict) - settings.update((k, v) for k, v in tempdict.iteritems() + + with open(filename) as f: + code = compile(f.read(), filename, 'exec') + exec(code, tempdict) + + settings.update((k, v) for k, v in tempdict.items() if k not in ['__builtins__']) # Make the paths relative to the settings file From 3e342b8ffb9aa5cfb38be670fce4ea40fae25268 Mon Sep 17 00:00:00 2001 From: Simon Conseil Date: Sun, 29 Sep 2013 00:20:35 +0200 Subject: [PATCH 2/5] Fix failing tests --- sigal/gallery.py | 11 ++++++++--- sigal/image.py | 6 ++---- sigal/video.py | 6 ++++++ 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/sigal/gallery.py b/sigal/gallery.py index c0e10c8..2be0f4e 100644 --- a/sigal/gallery.py +++ b/sigal/gallery.py @@ -73,7 +73,7 @@ class PathsDb(object): # basepath must to be a unicode string so that os.walk will return # unicode dirnames and filenames. If basepath is a str, we must # convert it to unicode. - if isinstance(path, str): + if compat.PY2 and isinstance(path, str): enc = locale.getpreferredencoding() self.basepath = path.decode(enc) else: @@ -105,8 +105,13 @@ class PathsDb(object): relpath = os.path.relpath(path, self.basepath) # sort images and sub-albums by name - filenames.sort(cmp=locale.strcoll) - dirnames.sort(cmp=locale.strcoll) + if compat.PY2: + filenames.sort(cmp=locale.strcoll) + dirnames.sort(cmp=locale.strcoll) + else: + from functools import cmp_to_key + filenames.sort(key=cmp_to_key(locale.strcoll)) + dirnames.sort(key=cmp_to_key(locale.strcoll)) self.db['paths_list'].append(relpath) self.db[relpath] = { diff --git a/sigal/image.py b/sigal/image.py index 3ea069a..86ed09f 100644 --- a/sigal/image.py +++ b/sigal/image.py @@ -91,8 +91,7 @@ def generate_image(source, outname, settings, options=None): outformat = img.format or original_format or 'JPEG' logger.debug(u'Save resized image to {0} ({1})'.format(outname, outformat)) - with open(outname, 'w') as fp: - save_image(img, fp, outformat, options=options, autoconvert=True) + save_image(img, outname, outformat, options=options, autoconvert=True) def generate_thumbnail(source, outname, box, fit=True, options=None): @@ -109,8 +108,7 @@ def generate_thumbnail(source, outname, box, fit=True, options=None): outformat = img.format or original_format or 'JPEG' logger.debug(u'Save thumnail image: {0} ({1})'.format(outname, outformat)) - with open(outname, 'w') as fp: - save_image(img, fp, outformat, options=options, autoconvert=True) + save_image(img, outname, outformat, options=options, autoconvert=True) def add_copyright(img, text): diff --git a/sigal/video.py b/sigal/video.py index 5ef4eb0..c3b2aad 100644 --- a/sigal/video.py +++ b/sigal/video.py @@ -27,6 +27,8 @@ import re import shutil import sigal.image +from . import compat + def vid_size(source): """Returns the dimensions of the video""" @@ -35,6 +37,10 @@ def vid_size(source): stdout=subprocess.PIPE, stderr=subprocess.PIPE) stdout, stderr = p.communicate() + + if not compat.PY2: + stderr = stderr.decode('utf8') + match = pattern.search(stderr) if match: x, y = int(match.groups()[0]), int(match.groups()[1]) From d8f9df2cd8b5b1cc364caccc9d7a08b83f786e8b Mon Sep 17 00:00:00 2001 From: Simon Conseil Date: Sun, 29 Sep 2013 00:39:50 +0200 Subject: [PATCH 3/5] Tox and travis config --- .gitignore | 3 ++- .travis.yml | 2 +- tox.ini | 14 ++++++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 tox.ini diff --git a/.gitignore b/.gitignore index dd77799..62103ed 100644 --- a/.gitignore +++ b/.gitignore @@ -3,8 +3,9 @@ *.pyc *_flymake .coverage -.sass-cache/ .ropeproject/ +.sass-cache/ +.tox/ nose.cfg MANIFEST _build/ diff --git a/.travis.yml b/.travis.yml index 7493fd0..61eb826 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: python python: - "2.7" - - "2.6" + - "3.3" before_install: # Dependencies to build PIL - sudo apt-get update -qq diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..c313f00 --- /dev/null +++ b/tox.ini @@ -0,0 +1,14 @@ +[tox] +envlist = py27,py33 + +[testenv] +commands = py.test +deps = + +[testenv:py27] +deps = + pytest + +[testenv:py33] +deps = + pytest From 4eb67c9162e764da9a3d40907e56cbf0330e5a0b Mon Sep 17 00:00:00 2001 From: Simon Conseil Date: Mon, 30 Sep 2013 22:53:24 +0200 Subject: [PATCH 4/5] Fix encoding issue with EXIF datetime --- sigal/__init__.py | 4 +--- sigal/image.py | 9 ++++++++- sigal/themes/colorbox/templates/index.html | 2 +- sigal/themes/galleria/templates/index.html | 2 +- 4 files changed, 11 insertions(+), 6 deletions(-) diff --git a/sigal/__init__.py b/sigal/__init__.py index 52fb2c6..99c97a1 100644 --- a/sigal/__init__.py +++ b/sigal/__init__.py @@ -41,7 +41,6 @@ from argh import ArghParser, arg from logging import Formatter from .gallery import Gallery -from .pkgmeta import __version__ from .settings import read_settings _DEFAULT_CONFIG_FILE = 'sigal.conf.py' @@ -151,7 +150,6 @@ def serve(path): def main(): - parser = ArghParser(description='Simple static gallery generator.', - version=__version__) + parser = ArghParser(description='Simple static gallery generator.') parser.add_commands([init, build, serve]) parser.dispatch() diff --git a/sigal/image.py b/sigal/image.py index 86ed09f..f505521 100644 --- a/sigal/image.py +++ b/sigal/image.py @@ -40,6 +40,8 @@ from pilkit.processors import Transpose, Adjust from pilkit.utils import save_image from datetime import datetime +from . import compat + def _has_exif_tags(img): return hasattr(img, 'info') and 'exif' in img.info @@ -183,7 +185,12 @@ def get_exif_tags(source): # Remove null bytes at the end if necessary date = data['DateTimeOriginal'].rsplit('\x00')[0] dt = datetime.strptime(date, '%Y:%m:%d %H:%M:%S') - simple['datetime'] = dt + dt = dt.strftime('%A, %d. %B %Y') + + if compat.PY2: + simple['datetime'] = dt.decode('utf8') + else: + simple['datetime'] = dt except (ValueError, TypeError) as e: msg = u'Could not parse DateTimeOriginal of %s: %s' % (source, e) logger.warning(msg) diff --git a/sigal/themes/colorbox/templates/index.html b/sigal/themes/colorbox/templates/index.html index f338175..bad0c7c 100644 --- a/sigal/themes/colorbox/templates/index.html +++ b/sigal/themes/colorbox/templates/index.html @@ -78,7 +78,7 @@ {% if media.big %} data-big="{{ media.big }}"{% endif %} {% if media.exif %} {% if media.exif.datetime %} - data-date=", {{ media.exif.datetime.strftime('%d %B %Y') }}" + data-date=", {{ media.exif.datetime }}" {% endif %} {% endif %} {%- endmacro %} diff --git a/sigal/themes/galleria/templates/index.html b/sigal/themes/galleria/templates/index.html index 1f22380..979a918 100644 --- a/sigal/themes/galleria/templates/index.html +++ b/sigal/themes/galleria/templates/index.html @@ -67,7 +67,7 @@ {% if media.exif.exposure %}Exposure: {{ media.exif.exposure }}, {% endif %} {% if media.exif.fstop %}Fstop: {{ media.exif.fstop }}{% endif %} {% if media.exif.datetime %} -
Date: {{ media.exif.datetime.strftime('%A, %d. %B %Y') }} +
Date: {{ media.exif.datetime }} {% endif %} {% endif %} {%- endmacro %} From 91e287be43b493eb9a598ad6989d0f631cb112c8 Mon Sep 17 00:00:00 2001 From: Simon Conseil Date: Mon, 30 Sep 2013 23:16:54 +0200 Subject: [PATCH 5/5] Re-add --version, update README & Changelog --- README.rst | 2 +- docs/changelog.rst | 7 +++++++ sigal/__init__.py | 3 +++ sigal/pkgmeta.py | 2 +- 4 files changed, 12 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 4dedc16..1ca4854 100644 --- a/README.rst +++ b/README.rst @@ -18,7 +18,7 @@ The idea behind Sigal is to ease the use of the javascript librairies like `galleria`_. These librairies do a great job to display the images, Sigal does what is missing: resize images, create thumbnails, generate html pages. -Sigal is currently compatible only with Python 2. +Sigal is compatible with Python 2.7 and 3.3. Links : diff --git a/docs/changelog.rst b/docs/changelog.rst index 9faf201..9cf35d6 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -2,6 +2,13 @@ Changelog =========== +Version 0.6.dev +~~~~~~~~~~~~~~~ + +Released on 2013-xx-xx. + +- Add support for Python 3.3 + Version 0.5.1 ~~~~~~~~~~~~~ diff --git a/sigal/__init__.py b/sigal/__init__.py index 99c97a1..eb60a00 100644 --- a/sigal/__init__.py +++ b/sigal/__init__.py @@ -41,6 +41,7 @@ from argh import ArghParser, arg from logging import Formatter from .gallery import Gallery +from .pkgmeta import __version__ from .settings import read_settings _DEFAULT_CONFIG_FILE = 'sigal.conf.py' @@ -152,4 +153,6 @@ def serve(path): def main(): parser = ArghParser(description='Simple static gallery generator.') parser.add_commands([init, build, serve]) + parser.add_argument('--version', action='version', + version='%(prog)s {}'.format(__version__)) parser.dispatch() diff --git a/sigal/pkgmeta.py b/sigal/pkgmeta.py index 0264f42..0c76bc3 100644 --- a/sigal/pkgmeta.py +++ b/sigal/pkgmeta.py @@ -2,7 +2,7 @@ __title__ = 'sigal' __author__ = 'Simon Conseil' -__version__ = '0.5.1' +__version__ = '0.6.0-dev' __license__ = 'MIT' __url__ = 'https://github.com/saimn/sigal' __all__ = ['__title__', '__author__', '__version__', '__license__', '__url__']