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>