summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Doc/library/glob.rst11
-rw-r--r--Lib/glob.py16
-rw-r--r--Lib/test/test_glob.py22
-rw-r--r--Misc/NEWS2
4 files changed, 49 insertions, 2 deletions
diff --git a/Doc/library/glob.rst b/Doc/library/glob.rst
index eff138b..57ccf4a 100644
--- a/Doc/library/glob.rst
+++ b/Doc/library/glob.rst
@@ -40,6 +40,17 @@ For example, ``'[?]'`` matches the character ``'?'``.
without actually storing them all simultaneously.
+.. function:: escape(pathname)
+
+ Escape all special characters (``'?'``, ``'*'`` and ``'['``).
+ This is useful if you want to match an arbitrary literal string that may
+ have special characters in it. Special characters in drive/UNC
+ sharepoints are not escaped, e.g. on Windows
+ ``escape('//?/c:/Quo vadis?.txt')`` returns ``'//?/c:/Quo vadis[?].txt'``.
+
+ .. versionadded:: 3.4
+
+
For example, consider a directory containing only the following files:
:file:`1.gif`, :file:`2.txt`, and :file:`card.gif`. :func:`glob` will produce
the following results. Notice how any leading components of the path are
diff --git a/Lib/glob.py b/Lib/glob.py
index 1a268a3..e388b5f 100644
--- a/Lib/glob.py
+++ b/Lib/glob.py
@@ -79,8 +79,8 @@ def glob0(dirname, basename):
return []
-magic_check = re.compile('[*?[]')
-magic_check_bytes = re.compile(b'[*?[]')
+magic_check = re.compile('([*?[])')
+magic_check_bytes = re.compile(b'([*?[])')
def has_magic(s):
if isinstance(s, bytes):
@@ -91,3 +91,15 @@ def has_magic(s):
def _ishidden(path):
return path[0] in ('.', b'.'[0])
+
+def escape(pathname):
+ """Escape all special characters.
+ """
+ # Escaping is done by wrapping any of "*?[" between square brackets.
+ # Metacharacters do not work in the drive part and shouldn't be escaped.
+ drive, pathname = os.path.splitdrive(pathname)
+ if isinstance(pathname, bytes):
+ pathname = magic_check_bytes.sub(br'[\1]', pathname)
+ else:
+ pathname = magic_check.sub(r'[\1]', pathname)
+ return drive + pathname
diff --git a/Lib/test/test_glob.py b/Lib/test/test_glob.py
index eb9aeb5..a5ab8d6 100644
--- a/Lib/test/test_glob.py
+++ b/Lib/test/test_glob.py
@@ -169,6 +169,28 @@ class GlobTests(unittest.TestCase):
eq(glob.glob('\\\\*\\*\\'), [])
eq(glob.glob(b'\\\\*\\*\\'), [])
+ def check_escape(self, arg, expected):
+ self.assertEqual(glob.escape(arg), expected)
+ self.assertEqual(glob.escape(os.fsencode(arg)), os.fsencode(expected))
+
+ def test_escape(self):
+ check = self.check_escape
+ check('abc', 'abc')
+ check('[', '[[]')
+ check('?', '[?]')
+ check('*', '[*]')
+ check('[[_/*?*/_]]', '[[][[]_/[*][?][*]/_]]')
+ check('/[[_/*?*/_]]/', '/[[][[]_/[*][?][*]/_]]/')
+
+ @unittest.skipUnless(sys.platform == "win32", "Win32 specific test")
+ def test_escape_windows(self):
+ check = self.check_escape
+ check('?:?', '?:[?]')
+ check('*:*', '*:[*]')
+ check(r'\\?\c:\?', r'\\?\c:\[?]')
+ check(r'\\*\*\*', r'\\*\*\[*]')
+ check('//?/c:/?', '//?/c:/[?]')
+ check('//*/*/*', '//*/*/[*]')
def test_main():
run_unittest(GlobTests)
diff --git a/Misc/NEWS b/Misc/NEWS
index 941eb29..7025410 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -50,6 +50,8 @@ Core and Builtins
Library
-------
+- Issue #8402: Added the escape() function to the glob module.
+
- Issue #17618: Add Base85 and Ascii85 encoding/decoding to the base64 module.
- Issue #19634: time.strftime("%y") now raises a ValueError on AIX when given a