Skip to content

Commit

Permalink
"Merge pull request #472 from marcellarius/fix-glob-ordering\n\nSort …
Browse files Browse the repository at this point in the history
…filenames when globbing"
  • Loading branch information
miracle2k committed Dec 25, 2016
2 parents a2136da + 4f7b8a6 commit f58e83f
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 11 deletions.
25 changes: 15 additions & 10 deletions src/webassets/env.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,14 +109,19 @@ class Resolver(object):
"""

def glob(self, basedir, expr):
"""Generator that runs when a glob expression needs to be
resolved. Yields a list of absolute filenames.
"""Evaluates a glob expression.
Yields a sorted list of absolute filenames.
"""
expr = path.join(basedir, expr)
for filename in glob.iglob(expr):
if path.isdir(filename):
continue
yield filename
def glob_generator(basedir, expr):
expr = path.join(basedir, expr)
for filename in glob.iglob(expr):
if path.isdir(filename):
continue
yield path.normpath(filename)

# The order of files returned by the glob implementation is undefined,
# so sort alphabetically to maintain a deterministic ordering
return sorted(glob_generator(basedir, expr))

def consider_single_directory(self, directory, item):
"""Searches for ``item`` within ``directory``. Is able to
Expand All @@ -128,7 +133,7 @@ def consider_single_directory(self, directory, item):
expr = path.join(directory, item)
if has_magic(expr):
# Note: No error if glob returns an empty list
return list(self.glob(directory, item))
return self.glob(directory, item)
else:
if path.exists(expr):
return expr
Expand All @@ -151,14 +156,14 @@ def search_load_path(self, ctx, item):
# We glob all paths.
result = []
for path in ctx.load_path:
result.extend(list(self.glob(path, item)))
result.extend(self.glob(path, item))
return result
else:
# Single file, stop when we find the first match, or error
# out otherwise. We still use glob() because then the load_path
# itself can contain globs. Neat!
for path in ctx.load_path:
result = list(self.glob(path, item))
result = self.glob(path, item)
if result:
return result
raise IOError("'%s' not found in load path: %s" % (
Expand Down
17 changes: 16 additions & 1 deletion tests/test_bundle_various.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

import copy
from os import path
import uuid
try:
from urllib.request import \
HTTPHandler, build_opener, install_opener, addinfourl
Expand Down Expand Up @@ -526,12 +527,26 @@ def test_do_not_glob_directories(self):
get_all_bundle_files(self.mkbundle('*'))))

def test_glob_exclude_output(self):
"""Never include the output file in the globbinb result.
"""Never include the output file in the globbing result.
"""
self.create_files(['out.js'])
assert not list(filter(lambda s: 'out.js' in s,
get_all_bundle_files(self.mkbundle('*', output='out.js'))))

def test_glob_ordering_consistent(self):
"""Glob results should be sorted alphabetically
"""
# Create randomly named files using a UUID for both name and contents.
unique_names = [uuid.uuid4().hex for i in range(10)]
files = {}
for name in unique_names:
files[name + ".uuid"] = name
self.create_files(files)
self.mkbundle("*.uuid", output="out").build()
content = self.get("out").split("\n")
expected_output = sorted(unique_names)
assert content == expected_output


class MockHTTPHandler(HTTPHandler):

Expand Down

0 comments on commit f58e83f

Please sign in to comment.