This looks useful.<br><br>Please post it as a feature request issue with patch on <a href="http://bugs.python.org">bugs.python.org</a>. Also, if you could include updates to the fnmatch documentation to describe exactly what your code allows that would help.<br>
<br>thanks,<br>-Greg<br><br><div class="gmail_quote">On Sat, Dec 6, 2008 at 8:13 PM, Erick Tryzelaar <span dir="ltr"><<a href="mailto:idadesub@users.sourceforge.net">idadesub@users.sourceforge.net</a>></span> wrote:<br>
<blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">My project needs to extend fnmatch to support zsh-style globbing,<br>
where you can use brackets to designate subexpressions. Say you had a<br>
directory structure like this:<br>
<br>
foo/<br>
foo.ext1<br>
foo.ext2<br>
bar/<br>
foo.ext1<br>
foo.ext2<br>
<br>
The subexpressions will let you do patterns like this:<br>
<br>
>>> glob.glob('foo/foo.{ext1,ext2}')<br>
['foo/foo.ext1', 'foo/foo.ext2']<br>
>>> glob.glob('foo/foo.ext{1,2}')<br>
['foo/foo.ext1', 'foo/foo.ext2']<br>
>>> glob.glob('{foo,bar}')<br>
['bar', 'foo']<br>
>>> glob.glob('{foo,bar}/foo*')<br>
['bar/foo.ext1', 'bar/foo.ext2', 'foo/foo.ext1', 'foo/foo.ext2']<br>
>>> glob.glob('{foo,bar}/foo.{ext*}')<br>
['bar/foo.ext1', 'bar/foo.ext2', 'foo/foo.ext1', 'foo/foo.ext2']<br>
>>> glob.glob('{f?o,b?r}/foo.{ext*}')<br>
['bar/foo.ext1', 'bar/foo.ext2', 'foo/foo.ext1', 'foo/foo.ext2']<br>
<br>
<br>
Would this be interesting to anyone else? It would unfortunately break<br>
fnmatch since it currently would ignore with {} in it. It'd be easy to<br>
work around that by adding a flag or using a different function name.<br>
Anyway, here's the patch against the head of py3k.<br>
<br>
-e<br>
<br>
<br>
<br>
Index: Lib/glob.py<br>
===================================================================<br>
--- Lib/glob.py (revision 67629)<br>
+++ Lib/glob.py (working copy)<br>
@@ -72,8 +72,8 @@<br>
return []<br>
<br>
<br>
-magic_check = re.compile('[*?[]')<br>
-magic_check_bytes = re.compile(b'[*?[]')<br>
+magic_check = re.compile('[*?[{]')<br>
+magic_check_bytes = re.compile(b'[*?[{]')<br>
<br>
def has_magic(s):<br>
if isinstance(s, bytes):<br>
Index: Lib/fnmatch.py<br>
===================================================================<br>
--- Lib/fnmatch.py (revision 67629)<br>
+++ Lib/fnmatch.py (working copy)<br>
@@ -22,10 +22,11 @@<br>
<br>
Patterns are Unix shell style:<br>
<br>
- * matches everything<br>
- ? matches any single character<br>
- [seq] matches any character in seq<br>
- [!seq] matches any char not in seq<br>
+ * matches everything<br>
+ ? matches any single character<br>
+ [seq] matches any character in seq<br>
+ [!seq] matches any char not in seq<br>
+ {pat1,pat2} matches subpattern pat1 or subpattern pat2<br>
<br>
An initial period in FILENAME is not special.<br>
Both FILENAME and PATTERN are first case-normalized<br>
@@ -84,10 +85,15 @@<br>
There is no way to quote meta-characters.<br>
"""<br>
<br>
- i, n = 0, len(pat)<br>
+ return _translate(0, pat, '')[2] + '$'<br>
+<br>
+def _translate(i, pat, end):<br>
res = ''<br>
+ n = len(pat)<br>
while i < n:<br>
c = pat[i]<br>
+ if c in end:<br>
+ return i, c, res<br>
i = i+1<br>
if c == '*':<br>
res = res + '.*'<br>
@@ -111,6 +117,27 @@<br>
elif stuff[0] == '^':<br>
stuff = '\\' + stuff<br>
res = '%s[%s]' % (res, stuff)<br>
+ elif c == '{':<br>
+ i, sub = _translate_subexpression(i, pat)<br>
+ res += sub<br>
else:<br>
res = res + re.escape(c)<br>
- return res + "$"<br>
+ return i, '', res<br>
+<br>
+def _translate_subexpression(i, pat):<br>
+ j = i<br>
+ subexpressions = []<br>
+ while True:<br>
+ j, c, res = _translate(j, pat, ',}')<br>
+ subexpressions.append(res)<br>
+<br>
+ if c == ',':<br>
+ j += 1<br>
+ elif c == '}':<br>
+ j += 1<br>
+ break<br>
+ else:<br>
+ # turns out we didn't have a subpattern<br>
+ return j, '{' + ','.join(subexpressions)<br>
+<br>
+ return j, '(' + '|'.join(subexpressions) + ')'<br>
Index: Lib/test/test_fnmatch.py<br>
===================================================================<br>
--- Lib/test/test_fnmatch.py (revision 67629)<br>
+++ Lib/test/test_fnmatch.py (working copy)<br>
@@ -37,6 +37,12 @@<br>
check('a', r'[!\]')<br>
check('\\', r'[!\]', 0)<br>
<br>
+ check('abcdefghi', 'ab{cd,12*}ef{gh?,34}')<br>
+ check('ab1234ef34', 'ab{cd,12*}ef{gh?,34}')<br>
+<br>
+ check('abcdefgh', 'ab{cd,12*}ef{gh?,34}', 0)<br>
+ check('ab1234ef345', 'ab{cd,12*}ef{gh?,34}', 0)<br>
+<br>
def test_mix_bytes_str(self):<br>
self.assertRaises(TypeError, fnmatch, 'test', b'*')<br>
self.assertRaises(TypeError, fnmatch, b'test', '*')<br>
Index: Lib/test/test_glob.py<br>
===================================================================<br>
--- Lib/test/test_glob.py (revision 67629)<br>
+++ Lib/test/test_glob.py (working copy)<br>
@@ -69,6 +69,7 @@<br>
eq(self.glob('aa?'), map(self.norm, ['aaa', 'aab']))<br>
eq(self.glob('aa[ab]'), map(self.norm, ['aaa', 'aab']))<br>
eq(self.glob('*q'), [])<br>
+ eq(self.glob('a{?a,?b}'), map(self.norm, ['aaa', 'aab']))<br>
<br>
def test_glob_nested_directory(self):<br>
eq = self.assertSequencesEqual_noorder<br>
@@ -89,6 +90,9 @@<br>
[self.norm('a', 'bcd', 'efg', 'ha')])<br>
eq(self.glob('?a?', '*F'), map(self.norm, [os.path.join('aaa', 'zzzF'),<br>
os.path.join('aab', 'F')]))<br>
+ eq(self.glob('a', 'b{c,x}d', '{*}', '*a'),<br>
+ [self.norm('a', 'bcd', 'efg', 'ha')])<br>
+ eq(self.glob('a', 'b{x,y}d', '{*}', '*a'), [])<br>
<br>
def test_glob_directory_with_trailing_slash(self):<br>
# We are verifying that when there is wildcard pattern which<br>
_______________________________________________<br>
Python-ideas mailing list<br>
<a href="mailto:Python-ideas@python.org">Python-ideas@python.org</a><br>
<a href="http://mail.python.org/mailman/listinfo/python-ideas" target="_blank">http://mail.python.org/mailman/listinfo/python-ideas</a><br>
</blockquote></div><br>