aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Lib/test/test_fnmatch.py
diff options
context:
space:
mode:
authorSerhiy Storchaka <storchaka@gmail.com>2022-06-05 11:46:29 +0300
committerGitHub <noreply@github.com>2022-06-05 11:46:29 +0300
commit0902c3d8edf7ef67972dd95f6a21670f5d1a4251 (patch)
tree08f8bbf28918ffc2efda41dcf9d1586636586d9b /Lib/test/test_fnmatch.py
parent6f8367d3489eff07139bc908fdf666fc904ca445 (diff)
downloadcpython-0902c3d8edf7ef67972dd95f6a21670f5d1a4251.tar.gz
cpython-0902c3d8edf7ef67972dd95f6a21670f5d1a4251.zip
gh-89973: Fix re.error in the fnmatch module. (GH-93072)
Character ranges with upper bound less that lower bound (e.g. [c-a]) are now interpreted as empty ranges, for compatibility with other glob pattern implementations. Previously it was re.error.
Diffstat (limited to 'Lib/test/test_fnmatch.py')
-rw-r--r--Lib/test/test_fnmatch.py114
1 files changed, 114 insertions, 0 deletions
diff --git a/Lib/test/test_fnmatch.py b/Lib/test/test_fnmatch.py
index ca695d6f3f0..10ed496d4e2 100644
--- a/Lib/test/test_fnmatch.py
+++ b/Lib/test/test_fnmatch.py
@@ -2,6 +2,7 @@
import unittest
import os
+import string
import warnings
from fnmatch import fnmatch, fnmatchcase, translate, filter
@@ -91,6 +92,119 @@ class FnmatchTestCase(unittest.TestCase):
check('usr/bin', 'usr\\bin', normsep)
check('usr\\bin', 'usr\\bin')
+ def test_char_set(self):
+ ignorecase = os.path.normcase('ABC') == os.path.normcase('abc')
+ check = self.check_match
+ tescases = string.ascii_lowercase + string.digits + string.punctuation
+ for c in tescases:
+ check(c, '[az]', c in 'az')
+ check(c, '[!az]', c not in 'az')
+ # Case insensitive.
+ for c in tescases:
+ check(c, '[AZ]', (c in 'az') and ignorecase)
+ check(c, '[!AZ]', (c not in 'az') or not ignorecase)
+ for c in string.ascii_uppercase:
+ check(c, '[az]', (c in 'AZ') and ignorecase)
+ check(c, '[!az]', (c not in 'AZ') or not ignorecase)
+ # Repeated same character.
+ for c in tescases:
+ check(c, '[aa]', c == 'a')
+ # Special cases.
+ for c in tescases:
+ check(c, '[^az]', c in '^az')
+ check(c, '[[az]', c in '[az')
+ check(c, r'[!]]', c != ']')
+ check('[', '[')
+ check('[]', '[]')
+ check('[!', '[!')
+ check('[!]', '[!]')
+
+ def test_range(self):
+ ignorecase = os.path.normcase('ABC') == os.path.normcase('abc')
+ normsep = os.path.normcase('\\') == os.path.normcase('/')
+ check = self.check_match
+ tescases = string.ascii_lowercase + string.digits + string.punctuation
+ for c in tescases:
+ check(c, '[b-d]', c in 'bcd')
+ check(c, '[!b-d]', c not in 'bcd')
+ check(c, '[b-dx-z]', c in 'bcdxyz')
+ check(c, '[!b-dx-z]', c not in 'bcdxyz')
+ # Case insensitive.
+ for c in tescases:
+ check(c, '[B-D]', (c in 'bcd') and ignorecase)
+ check(c, '[!B-D]', (c not in 'bcd') or not ignorecase)
+ for c in string.ascii_uppercase:
+ check(c, '[b-d]', (c in 'BCD') and ignorecase)
+ check(c, '[!b-d]', (c not in 'BCD') or not ignorecase)
+ # Upper bound == lower bound.
+ for c in tescases:
+ check(c, '[b-b]', c == 'b')
+ # Special cases.
+ for c in tescases:
+ check(c, '[!-#]', c not in '-#')
+ check(c, '[!--.]', c not in '-.')
+ check(c, '[^-`]', c in '^_`')
+ if not (normsep and c == '/'):
+ check(c, '[[-^]', c in r'[\]^')
+ check(c, r'[\-^]', c in r'\]^')
+ check(c, '[b-]', c in '-b')
+ check(c, '[!b-]', c not in '-b')
+ check(c, '[-b]', c in '-b')
+ check(c, '[!-b]', c not in '-b')
+ check(c, '[-]', c in '-')
+ check(c, '[!-]', c not in '-')
+ # Upper bound is less that lower bound: error in RE.
+ for c in tescases:
+ check(c, '[d-b]', False)
+ check(c, '[!d-b]', True)
+ check(c, '[d-bx-z]', c in 'xyz')
+ check(c, '[!d-bx-z]', c not in 'xyz')
+ check(c, '[d-b^-`]', c in '^_`')
+ if not (normsep and c == '/'):
+ check(c, '[d-b[-^]', c in r'[\]^')
+
+ def test_sep_in_char_set(self):
+ normsep = os.path.normcase('\\') == os.path.normcase('/')
+ check = self.check_match
+ check('/', r'[/]')
+ check('\\', r'[\]')
+ check('/', r'[\]', normsep)
+ check('\\', r'[/]', normsep)
+ check('[/]', r'[/]', False)
+ check(r'[\\]', r'[/]', False)
+ check('\\', r'[\t]')
+ check('/', r'[\t]', normsep)
+ check('t', r'[\t]')
+ check('\t', r'[\t]', False)
+
+ def test_sep_in_range(self):
+ normsep = os.path.normcase('\\') == os.path.normcase('/')
+ check = self.check_match
+ check('a/b', 'a[.-0]b', not normsep)
+ check('a\\b', 'a[.-0]b', False)
+ check('a\\b', 'a[Z-^]b', not normsep)
+ check('a/b', 'a[Z-^]b', False)
+
+ check('a/b', 'a[/-0]b', not normsep)
+ check(r'a\b', 'a[/-0]b', False)
+ check('a[/-0]b', 'a[/-0]b', False)
+ check(r'a[\-0]b', 'a[/-0]b', False)
+
+ check('a/b', 'a[.-/]b')
+ check(r'a\b', 'a[.-/]b', normsep)
+ check('a[.-/]b', 'a[.-/]b', False)
+ check(r'a[.-\]b', 'a[.-/]b', False)
+
+ check(r'a\b', r'a[\-^]b')
+ check('a/b', r'a[\-^]b', normsep)
+ check(r'a[\-^]b', r'a[\-^]b', False)
+ check('a[/-^]b', r'a[\-^]b', False)
+
+ check(r'a\b', r'a[Z-\]b', not normsep)
+ check('a/b', r'a[Z-\]b', False)
+ check(r'a[Z-\]b', r'a[Z-\]b', False)
+ check('a[Z-/]b', r'a[Z-\]b', False)
+
def test_warnings(self):
with warnings.catch_warnings():
warnings.simplefilter('error', Warning)