diff --git a/.travis.yml b/.travis.yml index 623c6e1..0e9c7e7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,6 @@ python: - "3.4" - "3.3" - "2.7" - - "2.6" install: - sudo apt-get install zlib1g-dev - sudo ln -s /usr/lib/i386-linux-gnu/libz.so /usr/lib diff --git a/glue/bin.py b/glue/bin.py index fe2c4c8..60ee6df 100755 --- a/glue/bin.py +++ b/glue/bin.py @@ -154,12 +154,12 @@ def add_deprecated_argument(*args, **kwargs): extra = 0 # Get the source from the source option or the first positional argument if not options.source and args: - options.source = args[0] + options.source = unicode(args[0]) extra += 1 # Get the output from the output option or the second positional argument if not options.output and args[extra:]: - options.output = args[extra] + options.output = unicode(args[extra]) # Check if source is available if options.source is None: diff --git a/glue/core.py b/glue/core.py index 52b9315..e2914b8 100644 --- a/glue/core.py +++ b/glue/core.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import re import os import sys @@ -5,6 +6,7 @@ import hashlib import StringIO import ConfigParser +import unicodedata from PIL import Image as PILImage @@ -48,7 +50,7 @@ def __init__(self, path, config): with open(self.path, "rb") as img: self._image_data = img.read() - print "\t{0} added to sprite".format(self.filename) + print u"\t{0} added to sprite".format(self.filename) @cached_property def image(self): @@ -225,7 +227,7 @@ def hash(self): hash_list.append(value) if sys.version < '3': - return hashlib.sha1(''.join(map(str, hash_list))).hexdigest()[:10] + return hashlib.sha1(''.join([h.encode('utf-8') if isinstance(h, unicode) else str(h) for h in hash_list])).hexdigest()[:10] return hashlib.sha1(''.join(map(str, hash_list)).encode('utf-8')).hexdigest()[:10] @cached_property @@ -258,11 +260,12 @@ def _locate_images(self): """ extensions = '|'.join(self.valid_extensions) extension_re = re.compile('.+\.(%s)$' % extensions, re.IGNORECASE) - files = sorted(os.listdir(self.path)) images = [] for root, dirs, files in os.walk(self.path, followlinks=self.config['follow_links']): for filename in sorted(files): + # normalize different unicode representations, see https://stackoverflow.com/a/33647372/193886 + filename = unicodedata.normalize('NFC', filename) if not filename.startswith('.') and extension_re.match(filename): images.append(Image(path=os.path.join(root, filename), config=self.config)) if not self.config['recursive']: diff --git a/glue/formats/css.py b/glue/formats/css.py index ecb7177..15adbc1 100644 --- a/glue/formats/css.py +++ b/glue/formats/css.py @@ -182,7 +182,7 @@ def apply_cachebuster(path): def generate_css_name(self, filename): filename = filename.rsplit('.', 1)[0] separator = self.sprite.config['css_separator'] - namespace = [re.sub(r'[^\w\-_]', '', filename)] + namespace = [re.sub(r'[^\w\-_]', '', filename, flags=re.UNICODE)] # Add sprite namespace if required if self.sprite.config['css_sprite_namespace']: diff --git a/glue/managers/watch.py b/glue/managers/watch.py index 51875eb..ca600c5 100644 --- a/glue/managers/watch.py +++ b/glue/managers/watch.py @@ -3,6 +3,7 @@ import time import signal import hashlib +import unicodedata class WatchManager(object): @@ -29,6 +30,7 @@ def generate_hash(self): hash_list = [] for root, dirs, files in os.walk(self.options['source']): for f in sorted([f for f in files if not f.startswith('.')]): + f = unicodedata.normalize('NFC', f) hash_list.append(os.path.join(root, f)) hash_list.append(str(os.path.getmtime(os.path.join(root, f)))) hash_list = ''.join(hash_list) diff --git a/tests.py b/tests.py index 556ec5a..3109bed 100644 --- a/tests.py +++ b/tests.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- import os import sys import json @@ -205,6 +206,31 @@ def test_output(self): u'width': u'64px', u'height': u'64px'}) + def test_unicode_input_output(self): + self.create_image(u"simple/redöåä.png", RED) + self.create_image(u"simple/blueöåä.png", BLUE) + code = self.call("glue simple output") + self.assertEqual(code, 0) + + self.assertExists("output/simple.png") + self.assertExists("output/simple.css") + self.assertColor("output/simple.png", RED, ((0, 0), (63, 63))) + self.assertColor("output/simple.png", BLUE, ((64, 0), (127, 63))) + + self.assertCSS(u"output/simple.css", u'.sprite-simple-redöåä', + {u'background-image': u"url(simple.png)", + u'background-repeat': u'no-repeat', + u'background-position': u'0 0', + u'width': u'64px', + u'height': u'64px'}) + + self.assertCSS(u"output/simple.css", u'.sprite-simple-blueöåä', + {u'background-image': u"url(simple.png)", + u'background-repeat': u'no-repeat', + u'background-position': u'-64px 0', + u'width': u'64px', + u'height': u'64px'}) + def test_quiet(self): self.create_image("simple/red.png", RED) self.create_image("simple/blue.png", BLUE) @@ -1801,5 +1827,6 @@ def test_caat_ratios(self): data = json.loads(f.read()) assert isinstance(data['sprites'], dict) + if __name__ == '__main__': unittest.main()