Merge pull request #40 from saimn/py3

Python 3.3 support
This commit is contained in:
Simon Conseil
2013-09-30 14:32:18 -07:00
15 changed files with 99 additions and 33 deletions

3
.gitignore vendored
View File

@@ -3,8 +3,9 @@
*.pyc
*_flymake
.coverage
.sass-cache/
.ropeproject/
.sass-cache/
.tox/
nose.cfg
MANIFEST
_build/

View File

@@ -1,7 +1,7 @@
language: python
python:
- "2.7"
- "2.6"
- "3.3"
before_install:
# Dependencies to build PIL
- sudo apt-get update -qq

View File

@@ -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 :

View File

@@ -2,6 +2,13 @@
Changelog
===========
Version 0.6.dev
~~~~~~~~~~~~~~~
Released on 2013-xx-xx.
- Add support for Python 3.3
Version 0.5.1
~~~~~~~~~~~~~

View File

@@ -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',

View File

@@ -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,13 +145,14 @@ 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)
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.add_argument('--version', action='version',
version='%(prog)s {}'.format(__version__))
parser.dispatch()

14
sigal/compat.py Normal file
View File

@@ -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

View File

@@ -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
@@ -69,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:
@@ -96,12 +100,18 @@ 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
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] = {

View File

@@ -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
@@ -91,8 +93,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 +110,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):
@@ -185,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)

View File

@@ -1,8 +1,8 @@
# -*- coding:utf-8 -*-
__title__ = 'sigal'
__author__ = u"Simon Conseil"
__version__ = "0.5.1"
__license__ = "MIT"
__author__ = 'Simon Conseil'
__version__ = '0.6.0-dev'
__license__ = 'MIT'
__url__ = 'https://github.com/saimn/sigal'
__all__ = ['__title__', '__author__', '__version__', '__license__', '__url__']

View File

@@ -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

View File

@@ -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 %}

View File

@@ -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 %}
<br>Date: {{ media.exif.datetime.strftime('%A, %d. %B %Y') }}
<br>Date: {{ media.exif.datetime }}
{% endif %}
{% endif %}
{%- endmacro %}

View File

@@ -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])

14
tox.ini Normal file
View File

@@ -0,0 +1,14 @@
[tox]
envlist = py27,py33
[testenv]
commands = py.test
deps =
[testenv:py27]
deps =
pytest
[testenv:py33]
deps =
pytest