pax_global_header 0000666 0000000 0000000 00000000064 13007076126 0014514 g ustar 00root root 0000000 0000000 52 comment=4851f8c82363113db551f86e612dc8d32b38692f
python-glob2-0.5/ 0000775 0000000 0000000 00000000000 13007076126 0013704 5 ustar 00root root 0000000 0000000 python-glob2-0.5/.gitignore 0000664 0000000 0000000 00000000166 13007076126 0015677 0 ustar 00root root 0000000 0000000 *.pyc
/LOCAL_TODO
# distutils/setuptools
/dist/
*egg-info
# IDEs
*.wpr
/.idea/
# Folder config file
[Dd]esktop.ini
python-glob2-0.5/.idea/ 0000775 0000000 0000000 00000000000 13007076126 0014664 5 ustar 00root root 0000000 0000000 python-glob2-0.5/.idea/encodings.xml 0000664 0000000 0000000 00000000246 13007076126 0017361 0 ustar 00root root 0000000 0000000
python-glob2-0.5/.idea/modules.xml 0000664 0000000 0000000 00000000426 13007076126 0017060 0 ustar 00root root 0000000 0000000
python-glob2-0.5/.idea/python-glob2.iml 0000664 0000000 0000000 00000001052 13007076126 0017711 0 ustar 00root root 0000000 0000000
python-glob2-0.5/CHANGES 0000664 0000000 0000000 00000000431 13007076126 0014675 0 ustar 00root root 0000000 0000000 0.5 (2016-11-04)
- include_hidden option.
- Python 3 fixes.
- Publish a wheel.
0.4 (2013-05-08)
- Support Python 3.
0.3 (2012-01-19)
- Fix non-glob patterns (patch by Zalan).
- Don't shadow internal "glob" module.
0.2 (2011-06-14)
- Initial release.
python-glob2-0.5/LICENSE 0000664 0000000 0000000 00000002517 13007076126 0014716 0 ustar 00root root 0000000 0000000 Copyright (c) 2008, Michael Elsdörfer
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials
provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE. python-glob2-0.5/MANIFEST.in 0000664 0000000 0000000 00000000063 13007076126 0015441 0 ustar 00root root 0000000 0000000 include README.rst CHANGES LICENSE
include test.py
python-glob2-0.5/README.rst 0000664 0000000 0000000 00000004651 13007076126 0015401 0 ustar 00root root 0000000 0000000 python-glob2
============
This is an extended version of Python's builtin glob module
(http://docs.python.org/library/glob.html) which adds:
- The ability to capture the text matched by glob patterns, and
return those matches alongside the filenames.
- A recursive '**' globbing syntax, akin for example to the ``globstar``
option of the bash shell.
- The ability to replace the filesystem functions used, in order to glob
on virtual filesystems.
- Compatible with Python 2 and Python 3 (tested with 3.3).
It's currently based on the glob code from Python 3.3.1.
Examples
--------
Matches being returned:
~~~~~~~~~~~~~~~~~~~~~~~
::
import glob2
for filename, (version,) in glob2.iglob('./binaries/project-*.zip', with_matches=True):
print version
Recursive glob:
~~~~~~~~~~~~~~~
::
>>> import glob2
>>> all_header_files = glob2.glob('src/**/*.h')
['src/fs.h', 'src/media/mp3.h', 'src/media/mp3/frame.h', ...]
Note that ``**`` must appear on it's own as a directory
element to have its special meaning. ``**h`` will not have the
desired effect.
``**`` will match ".", so ``**/*.py`` returns Python files in the
current directory. If this is not wanted, ``*/**/*.py`` should be used
instead.
Custom Globber:
~~~~~~~~~~~~~~~
::
from glob2 import Globber
class VirtualStorageGlobber(Globber):
def __init__(self, storage):
self.storage = storage
def listdir(self, path):
# Must raise os.error if path is not a directory
return self.storage.listdir(path)
def exists(self, path):
return self.storage.exists(path)
def isdir(self, path):
# Used only for trailing slash syntax (``foo/``).
return self.storage.isdir(path)
def islink(self, path):
# Used only for recursive glob (``**``).
return self.storage.islink(path)
globber = VirtualStorageGlobber(sftp_storage)
globber.glob('/var/www/**/*.js')
If ``isdir`` and/or ``islink`` cannot be implemented for a storage, you can
make them return a fixed value, with the following consequences:
- If ``isdir`` returns ``True``, a glob expression ending with a slash
will return all items, even non-directories, if it returns ``False``,
the same glob expression will return nothing.
- Return ``islink`` ``True``, the recursive globbing syntax ** will
follow all links. If you return ``False``, it will not work at all.
python-glob2-0.5/TODO 0000664 0000000 0000000 00000000362 13007076126 0014375 0 ustar 00root root 0000000 0000000 Because our implementation of recursive directory search (**) using
os.walk, and the matching using fnmatch, are both not using iterators,
something like /** currently needs to read the whole filesystem into
memory before returning anything.
python-glob2-0.5/glob2/ 0000775 0000000 0000000 00000000000 13007076126 0014711 5 ustar 00root root 0000000 0000000 python-glob2-0.5/glob2/__init__.py 0000664 0000000 0000000 00000000122 13007076126 0017015 0 ustar 00root root 0000000 0000000 from __future__ import absolute_import
from .impl import *
__version__ = (0, 5)
python-glob2-0.5/glob2/compat.py 0000664 0000000 0000000 00000015313 13007076126 0016551 0 ustar 00root root 0000000 0000000 # Back-port functools.lru_cache to Python 2 (and <= 3.2)
# {{{ http://code.activestate.com/recipes/578078/ (r6)
from collections import namedtuple
from functools import update_wrapper
from threading import RLock
_CacheInfo = namedtuple("CacheInfo", ["hits", "misses", "maxsize", "currsize"])
class _HashedSeq(list):
__slots__ = 'hashvalue'
def __init__(self, tup, hash=hash):
self[:] = tup
self.hashvalue = hash(tup)
def __hash__(self):
return self.hashvalue
def _make_key(args, kwds, typed,
kwd_mark = (object(),),
fasttypes = set((int, str, frozenset, type(None))),
sorted=sorted, tuple=tuple, type=type, len=len):
'Make a cache key from optionally typed positional and keyword arguments'
key = args
if kwds:
sorted_items = sorted(kwds.items())
key += kwd_mark
for item in sorted_items:
key += item
if typed:
key += tuple(type(v) for v in args)
if kwds:
key += tuple(type(v) for k, v in sorted_items)
elif len(key) == 1 and type(key[0]) in fasttypes:
return key[0]
return _HashedSeq(key)
def lru_cache(maxsize=100, typed=False):
"""Least-recently-used cache decorator.
If *maxsize* is set to None, the LRU features are disabled and the cache
can grow without bound.
If *typed* is True, arguments of different types will be cached separately.
For example, f(3.0) and f(3) will be treated as distinct calls with
distinct results.
Arguments to the cached function must be hashable.
View the cache statistics named tuple (hits, misses, maxsize, currsize) with
f.cache_info(). Clear the cache and statistics with f.cache_clear().
Access the underlying function with f.__wrapped__.
See: http://en.wikipedia.org/wiki/Cache_algorithms#Least_Recently_Used
"""
# Users should only access the lru_cache through its public API:
# cache_info, cache_clear, and f.__wrapped__
# The internals of the lru_cache are encapsulated for thread safety and
# to allow the implementation to change (including a possible C version).
def decorating_function(user_function):
cache = dict()
stats = [0, 0] # make statistics updateable non-locally
HITS, MISSES = 0, 1 # names for the stats fields
make_key = _make_key
cache_get = cache.get # bound method to lookup key or return None
_len = len # localize the global len() function
lock = RLock() # because linkedlist updates aren't threadsafe
root = [] # root of the circular doubly linked list
root[:] = [root, root, None, None] # initialize by pointing to self
nonlocal_root = [root] # make updateable non-locally
PREV, NEXT, KEY, RESULT = 0, 1, 2, 3 # names for the link fields
if maxsize == 0:
def wrapper(*args, **kwds):
# no caching, just do a statistics update after a successful call
result = user_function(*args, **kwds)
stats[MISSES] += 1
return result
elif maxsize is None:
def wrapper(*args, **kwds):
# simple caching without ordering or size limit
key = make_key(args, kwds, typed)
result = cache_get(key, root) # root used here as a unique not-found sentinel
if result is not root:
stats[HITS] += 1
return result
result = user_function(*args, **kwds)
cache[key] = result
stats[MISSES] += 1
return result
else:
def wrapper(*args, **kwds):
# size limited caching that tracks accesses by recency
key = make_key(args, kwds, typed) if kwds or typed else args
with lock:
link = cache_get(key)
if link is not None:
# record recent use of the key by moving it to the front of the list
root, = nonlocal_root
link_prev, link_next, key, result = link
link_prev[NEXT] = link_next
link_next[PREV] = link_prev
last = root[PREV]
last[NEXT] = root[PREV] = link
link[PREV] = last
link[NEXT] = root
stats[HITS] += 1
return result
result = user_function(*args, **kwds)
with lock:
root, = nonlocal_root
if key in cache:
# getting here means that this same key was added to the
# cache while the lock was released. since the link
# update is already done, we need only return the
# computed result and update the count of misses.
pass
elif _len(cache) >= maxsize:
# use the old root to store the new key and result
oldroot = root
oldroot[KEY] = key
oldroot[RESULT] = result
# empty the oldest link and make it the new root
root = nonlocal_root[0] = oldroot[NEXT]
oldkey = root[KEY]
oldvalue = root[RESULT]
root[KEY] = root[RESULT] = None
# now update the cache dictionary for the new links
del cache[oldkey]
cache[key] = oldroot
else:
# put result in a new link at the front of the list
last = root[PREV]
link = [last, root, key, result]
last[NEXT] = root[PREV] = cache[key] = link
stats[MISSES] += 1
return result
def cache_info():
"""Report cache statistics"""
with lock:
return _CacheInfo(stats[HITS], stats[MISSES], maxsize, len(cache))
def cache_clear():
"""Clear the cache and cache statistics"""
with lock:
cache.clear()
root = nonlocal_root[0]
root[:] = [root, root, None, None]
stats[:] = [0, 0]
wrapper.__wrapped__ = user_function
wrapper.cache_info = cache_info
wrapper.cache_clear = cache_clear
return update_wrapper(wrapper, user_function)
return decorating_function python-glob2-0.5/glob2/fnmatch.py 0000664 0000000 0000000 00000006346 13007076126 0016714 0 ustar 00root root 0000000 0000000 """Filename matching with shell patterns.
fnmatch(FILENAME, PATTERN) matches according to the local convention.
fnmatchcase(FILENAME, PATTERN) always takes case in account.
The functions operate by translating the pattern into a regular
expression. They cache the compiled regular expressions for speed.
The function translate(PATTERN) returns a regular expression
corresponding to PATTERN. (It does not compile it.)
"""
import os
import posixpath
import re
try:
from functools import lru_cache
except ImportError:
from .compat import lru_cache
__all__ = ["filter", "fnmatch", "fnmatchcase", "translate"]
def fnmatch(name, pat):
"""Test whether FILENAME matches PATTERN.
Patterns are Unix shell style:
* matches everything
? matches any single character
[seq] matches any character in seq
[!seq] matches any char not in seq
An initial period in FILENAME is not special.
Both FILENAME and PATTERN are first case-normalized
if the operating system requires it.
If you don't want this, use fnmatchcase(FILENAME, PATTERN).
"""
name = os.path.normcase(name)
pat = os.path.normcase(pat)
return fnmatchcase(name, pat)
lru_cache(maxsize=256, typed=True)
def _compile_pattern(pat):
if isinstance(pat, bytes):
pat_str = pat.decode('ISO-8859-1')
res_str = translate(pat_str)
res = res_str.encode('ISO-8859-1')
else:
res = translate(pat)
return re.compile(res).match
def filter(names, pat):
"""Return the subset of the list NAMES that match PAT."""
result = []
pat = os.path.normcase(pat)
match = _compile_pattern(pat)
if os.path is posixpath:
# normcase on posix is NOP. Optimize it away from the loop.
for name in names:
m = match(name)
if m:
result.append((name, m.groups()))
else:
for name in names:
m = match(os.path.normcase(name))
if m:
result.append((name, m.groups()))
return result
def fnmatchcase(name, pat):
"""Test whether FILENAME matches PATTERN, including case.
This is a version of fnmatch() which doesn't case-normalize
its arguments.
"""
match = _compile_pattern(pat)
return match(name) is not None
def translate(pat):
"""Translate a shell PATTERN to a regular expression.
There is no way to quote meta-characters.
"""
i, n = 0, len(pat)
res = ''
while i < n:
c = pat[i]
i = i+1
if c == '*':
res = res + '(.*)'
elif c == '?':
res = res + '(.)'
elif c == '[':
j = i
if j < n and pat[j] == '!':
j = j+1
if j < n and pat[j] == ']':
j = j+1
while j < n and pat[j] != ']':
j = j+1
if j >= n:
res = res + '\\['
else:
stuff = pat[i:j].replace('\\','\\\\')
i = j+1
if stuff[0] == '!':
stuff = '^' + stuff[1:]
elif stuff[0] == '^':
stuff = '\\' + stuff
res = '%s([%s])' % (res, stuff)
else:
res = res + re.escape(c)
return res + '\Z(?ms)'
python-glob2-0.5/glob2/impl.py 0000664 0000000 0000000 00000016475 13007076126 0016241 0 ustar 00root root 0000000 0000000 """Filename globbing utility."""
from __future__ import absolute_import
import sys
import os
import re
from . import fnmatch
try:
from itertools import imap
except ImportError:
imap = map
class Globber(object):
listdir = staticmethod(os.listdir)
isdir = staticmethod(os.path.isdir)
islink = staticmethod(os.path.islink)
exists = staticmethod(os.path.lexists)
def walk(self, top, followlinks=False):
"""A simplified version of os.walk (code copied) that uses
``self.listdir``, and the other local filesystem methods.
Because we don't care about file/directory distinctions, only
a single list is returned.
"""
try:
names = self.listdir(top)
except os.error as err:
return
items = []
for name in names:
items.append(name)
yield top, items
for name in items:
new_path = os.path.join(top, name)
if followlinks or not self.islink(new_path):
for x in self.walk(new_path, followlinks):
yield x
def glob(self, pathname, with_matches=False, include_hidden=False):
"""Return a list of paths matching a pathname pattern.
The pattern may contain simple shell-style wildcards a la
fnmatch. However, unlike fnmatch, filenames starting with a
dot are special cases that are not matched by '*' and '?'
patterns.
If ``include_hidden`` is True, then files and folders starting with
a dot are also returned.
"""
return list(self.iglob(pathname, with_matches, include_hidden))
def iglob(self, pathname, with_matches=False, include_hidden=False):
"""Return an iterator which yields the paths matching a pathname
pattern.
The pattern may contain simple shell-style wildcards a la
fnmatch. However, unlike fnmatch, filenames starting with a
dot are special cases that are not matched by '*' and '?'
patterns.
If ``with_matches`` is True, then for each matching path
a 2-tuple will be returned; the second element if the tuple
will be a list of the parts of the path that matched the individual
wildcards.
If ``include_hidden`` is True, then files and folders starting with
a dot are also returned.
"""
result = self._iglob(pathname, include_hidden=include_hidden)
if with_matches:
return result
return imap(lambda s: s[0], result)
def _iglob(self, pathname, rootcall=True, include_hidden=False):
"""Internal implementation that backs :meth:`iglob`.
``rootcall`` is required to differentiate between the user's call to
iglob(), and subsequent recursive calls, for the purposes of resolving
certain special cases of ** wildcards. Specifically, "**" is supposed
to include the current directory for purposes of globbing, but the
directory itself should never be returned. So if ** is the lastmost
part of the ``pathname`` given the user to the root call, we want to
ignore the current directory. For this, we need to know which the root
call is.
"""
# Short-circuit if no glob magic
if not has_magic(pathname):
if self.exists(pathname):
yield pathname, ()
return
# If no directory part is left, assume the working directory
dirname, basename = os.path.split(pathname)
# If the directory is globbed, recurse to resolve.
# If at this point there is no directory part left, we simply
# continue with dirname="", which will search the current dir.
# `os.path.split()` returns the argument itself as a dirname if it is a
# drive or UNC path. Prevent an infinite recursion if a drive or UNC path
# contains magic characters (i.e. r'\\?\C:').
if dirname != pathname and has_magic(dirname):
# Note that this may return files, which will be ignored
# later when we try to use them as directories.
# Prefiltering them here would only require more IO ops.
dirs = self._iglob(dirname, False, include_hidden)
else:
dirs = [(dirname, ())]
# Resolve ``basename`` expr for every directory found
for dirname, dir_groups in dirs:
for name, groups in self.resolve_pattern(
dirname, basename, not rootcall, include_hidden):
yield os.path.join(dirname, name), dir_groups + groups
def resolve_pattern(self, dirname, pattern, globstar_with_root, include_hidden):
"""Apply ``pattern`` (contains no path elements) to the
literal directory in ``dirname``.
If pattern=='', this will filter for directories. This is
a special case that happens when the user's glob expression ends
with a slash (in which case we only want directories). It simpler
and faster to filter here than in :meth:`_iglob`.
"""
if sys.version_info[0] == 3:
if isinstance(pattern, bytes):
dirname = bytes(os.curdir, 'ASCII')
else:
if isinstance(pattern, unicode) and not isinstance(dirname, unicode):
dirname = unicode(dirname, sys.getfilesystemencoding() or
sys.getdefaultencoding())
# If no magic, short-circuit, only check for existence
if not has_magic(pattern):
if pattern == '':
if self.isdir(dirname):
return [(pattern, ())]
else:
if self.exists(os.path.join(dirname, pattern)):
return [(pattern, ())]
return []
if not dirname:
dirname = os.curdir
try:
if pattern == '**':
# Include the current directory in **, if asked; by adding
# an empty string as opposed to '.', we spare ourselves
# having to deal with os.path.normpath() later.
names = [''] if globstar_with_root else []
for top, entries in self.walk(dirname):
_mkabs = lambda s: os.path.join(top[len(dirname)+1:], s)
names.extend(map(_mkabs, entries))
# Reset pattern so that fnmatch(), which does not understand
# ** specifically, will only return a single group match.
pattern = '*'
else:
names = self.listdir(dirname)
except os.error:
return []
if not include_hidden and not _ishidden(pattern):
# Remove hidden files, but take care to ensure
# that the empty string we may have added earlier remains.
# Do not filter out the '' that we might have added earlier
names = filter(lambda x: not x or not _ishidden(x), names)
return fnmatch.filter(names, pattern)
default_globber = Globber()
glob = default_globber.glob
iglob = default_globber.iglob
del default_globber
magic_check = re.compile('[*?[]')
magic_check_bytes = re.compile(b'[*?[]')
def has_magic(s):
if isinstance(s, bytes):
match = magic_check_bytes.search(s)
else:
match = magic_check.search(s)
return match is not None
def _ishidden(path):
return path[0] in ('.', b'.'[0])
python-glob2-0.5/setup.cfg 0000664 0000000 0000000 00000000034 13007076126 0015522 0 ustar 00root root 0000000 0000000 [bdist_wheel]
universal = 1
python-glob2-0.5/setup.py 0000775 0000000 0000000 00000002313 13007076126 0015420 0 ustar 00root root 0000000 0000000 #!/usr/bin/env python
import os
from setuptools import setup, find_packages
# Figure out the version
import re
here = os.path.dirname(os.path.abspath(__file__))
version_re = re.compile(
r'__version__ = (\(.*?\))')
fp = open(os.path.join(here, 'glob2', '__init__.py'))
version = None
for line in fp:
match = version_re.search(line)
if match:
version = eval(match.group(1))
break
else:
raise Exception("Cannot find version in __init__.py")
fp.close()
setup(
name = 'glob2',
version = ".".join(map(str, version)),
description = 'Version of the glob module that can capture patterns '+
'and supports recursive wildcards',
author = 'Michael Elsdoerfer',
author_email = 'michael@elsdoerfer.com',
license='BSD',
url = 'http://github.com/miracle2k/python-glob2/',
classifiers = [
'Development Status :: 3 - Alpha',
'Intended Audience :: Developers',
'License :: OSI Approved :: BSD License',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 3',
'Topic :: Software Development :: Libraries',
],
packages = find_packages()
)
python-glob2-0.5/test.py 0000664 0000000 0000000 00000011704 13007076126 0015240 0 ustar 00root root 0000000 0000000 import os
from os import path
import shutil
import tempfile
import glob2
from glob2 import fnmatch
class TestFnmatch(object):
def test_filter_everything(self):
names = (
'fooABC', 'barABC', 'foo',)
assert fnmatch.filter(names, 'foo*') == [
('fooABC', ('ABC',)),
('foo', ('',))
]
assert fnmatch.filter(names, '*AB*') == [
('fooABC', ('foo', 'C')),
('barABC', ('bar', 'C'))
]
def test_filter_single_character(self):
names = (
'fooA', 'barA', 'foo',)
assert fnmatch.filter(names, 'foo?') == [
('fooA', ('A',)),
]
assert fnmatch.filter(names, '???A') == [
('fooA', ('f', 'o', 'o',)),
('barA', ('b', 'a', 'r',)),
]
def test_sequence(self):
names = (
'fooA', 'fooB', 'fooC', 'foo',)
assert fnmatch.filter(names, 'foo[AB]') == [
('fooA', ('A',)),
('fooB', ('B',)),
]
assert fnmatch.filter(names, 'foo[!AB]') == [
('fooC', ('C',)),
]
class BaseTest(object):
def setup(self):
self.basedir = tempfile.mkdtemp()
self._old_cwd = os.getcwd()
os.chdir(self.basedir)
self.setup_files()
def setup_files(self):
pass
def teardown(self):
os.chdir(self._old_cwd)
shutil.rmtree(self.basedir)
def makedirs(self, *names):
for name in names:
os.makedirs(path.join(self.basedir, name))
def touch(self, *names):
for name in names:
open(path.join(self.basedir, name), 'w').close()
class TestPatterns(BaseTest):
def test(self):
self.makedirs('dir1', 'dir22')
self.touch(
'dir1/a-file', 'dir1/b-file', 'dir22/a-file', 'dir22/b-file')
assert glob2.glob('dir?/a-*', True) == [
('dir1/a-file', ('1', 'file'))
]
class TestRecursive(BaseTest):
def setup_files(self):
self.makedirs('a', 'b', 'a/foo')
self.touch('file.py', 'file.txt', 'a/bar.py', 'README', 'b/py',
'b/bar.py', 'a/foo/hello.py', 'a/foo/world.txt')
def test_recursive(self):
# ** includes the current directory
assert sorted(glob2.glob('**/*.py', True)) == [
('a/bar.py', ('a', 'bar')),
('a/foo/hello.py', ('a/foo', 'hello')),
('b/bar.py', ('b', 'bar')),
('file.py', ('', 'file')),
]
def test_exclude_root_directory(self):
# If files from the root directory should not be included,
# this is the syntax to use:
assert sorted(glob2.glob('*/**/*.py', True)) == [
('a/bar.py', ('a', '', 'bar')),
('a/foo/hello.py', ('a', 'foo', 'hello')),
('b/bar.py', ('b', '', 'bar'))
]
def test_only_directories(self):
# Return directories only
assert sorted(glob2.glob('**/', True)) == [
('a/', ('a',)),
('a/foo/', ('a/foo',)),
('b/', ('b',)),
]
def test_parent_dir(self):
# Make sure ".." can be used
os.chdir(path.join(self.basedir, 'b'))
assert sorted(glob2.glob('../a/**/*.py', True)), [
('../a/bar.py', ('', 'bar')),
('../a/foo/hello.py', ('foo', 'hello'))
]
def test_fixed_basename(self):
assert sorted(glob2.glob('**/bar.py', True)) == [
('a/bar.py', ('a',)),
('b/bar.py', ('b',)),
]
def test_all_files(self):
# Return all files
os.chdir(path.join(self.basedir, 'a'))
assert sorted(glob2.glob('**', True)) == [
('bar.py', ('bar.py',)),
('foo', ('foo',)),
('foo/hello.py', ('foo/hello.py',)),
('foo/world.txt', ('foo/world.txt',)),
]
def test_root_directory_not_returned(self):
# Ensure that a certain codepath (when the basename is globbed
# with ** as opposed to the dirname) does not cause
# the root directory to be part of the result.
# -> b/ is NOT in the result!
assert sorted(glob2.glob('b/**', True)) == [
('b/bar.py', ('bar.py',)),
('b/py', ('py',)),
]
def test_non_glob(self):
# Test without patterns.
assert glob2.glob(__file__, True) == [
(__file__, ())
]
assert glob2.glob(__file__) == [
(__file__)
]
class TestIncludeHidden(BaseTest):
def setup_files(self):
self.makedirs('a', 'b', 'a/.foo')
self.touch('file.py', 'file.txt', 'a/.bar', 'README', 'b/py',
'b/.bar', 'a/.foo/hello.py', 'a/.foo/world.txt')
def test_hidden(self):
# ** includes the current directory
assert sorted(glob2.glob('*/*', True, include_hidden=True)), [
('a/.bar', ('a', '.bar')),
('a/.foo', ('a', '.foo')),
('b/.bar', ('b', '.bar')),
('b/py', ('b', 'py')),
]