diff --git a/Doc/library/glob.rst b/Doc/library/glob.rst index 684466d354aef8..d4850445db777e 100644 --- a/Doc/library/glob.rst +++ b/Doc/library/glob.rst @@ -38,7 +38,7 @@ The :mod:`glob` module defines the following functions: .. function:: glob(pathname, *, root_dir=None, dir_fd=None, recursive=False, \ - include_hidden=False) + include_hidden=False, expand_tilde=False) Return a possibly empty list of path names that match *pathname*, which must be a string containing a path specification. *pathname* can be either absolute @@ -68,6 +68,8 @@ The :mod:`glob` module defines the following functions: If *include_hidden* is true, "``**``" pattern will match hidden directories. + If *expand_tilde* is true, ``~`` will be expanded to the users home directory. + .. audit-event:: glob.glob pathname,recursive glob.glob .. audit-event:: glob.glob/2 pathname,recursive,root_dir,dir_fd glob.glob @@ -88,6 +90,9 @@ The :mod:`glob` module defines the following functions: .. versionchanged:: 3.11 Added the *include_hidden* parameter. + .. versionchanged:: next + Added the *expand_tilde* parameter. + .. function:: iglob(pathname, *, root_dir=None, dir_fd=None, recursive=False, \ include_hidden=False) diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index f02ce6bc1d4f2f..91f1a0c4dd5825 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -741,6 +741,13 @@ getopt (Contributed by Serhiy Storchaka in :gh:`126390`.) +glob +---- + +* Add *expand_tilde* option to :func:`~glob.glob`. + (Contributed by Stan Ulbrych in :gh:`132757`.) + + graphlib -------- diff --git a/Lib/glob.py b/Lib/glob.py index 8879eff80415aa..0b1758a7d2a89a 100644 --- a/Lib/glob.py +++ b/Lib/glob.py @@ -14,7 +14,7 @@ __all__ = ["glob", "iglob", "escape", "translate"] def glob(pathname, *, root_dir=None, dir_fd=None, recursive=False, - include_hidden=False): + include_hidden=False, expand_tilde=False): """Return a list of paths matching a pathname pattern. The pattern may contain simple shell-style wildcards a la @@ -29,10 +29,10 @@ def glob(pathname, *, root_dir=None, dir_fd=None, recursive=False, zero or more directories and subdirectories. """ return list(iglob(pathname, root_dir=root_dir, dir_fd=dir_fd, recursive=recursive, - include_hidden=include_hidden)) + include_hidden=include_hidden, expand_tilde=expand_tilde)) def iglob(pathname, *, root_dir=None, dir_fd=None, recursive=False, - include_hidden=False): + include_hidden=False, expand_tilde=False): """Return an iterator which yields the paths matching a pathname pattern. The pattern may contain simple shell-style wildcards a la @@ -45,6 +45,14 @@ def iglob(pathname, *, root_dir=None, dir_fd=None, recursive=False, """ sys.audit("glob.glob", pathname, recursive) sys.audit("glob.glob/2", pathname, recursive, root_dir, dir_fd) + + if expand_tilde: + tilde = '~' if isinstance(pathname, str) else b'~' + sep = os.path.sep if isinstance(pathname, str) else os.path.sep.encode('ascii') + home = str(os.path.expanduser('~')) if isinstance(pathname, str) else os.path.expanduser('~').encode('ascii') + if pathname == tilde or pathname.startswith(tilde + sep): + pathname = pathname.replace(tilde, home, 1) + if root_dir is not None: root_dir = os.fspath(root_dir) else: diff --git a/Lib/test/test_glob.py b/Lib/test/test_glob.py index 6e5fc2939c6f2c..a4551901d48b8d 100644 --- a/Lib/test/test_glob.py +++ b/Lib/test/test_glob.py @@ -4,6 +4,7 @@ import shutil import sys import unittest +import unittest.mock import warnings from test.support import is_wasi, Py_DEBUG @@ -210,6 +211,21 @@ def test_glob_bytes_directory_with_trailing_slash(self): [os.fsencode(self.norm('aaa') + os.sep), os.fsencode(self.norm('aab') + os.sep)]) + def test_glob_tilde_expansion(self): + with unittest.mock.patch('os.path.expanduser', return_value=self.tempdir): + results = glob.glob('~', expand_tilde=True) + self.assertEqual([self.tempdir], results) + + results = glob.glob(f'~{os.sep}*', expand_tilde=True) + self.assertIn(self.tempdir + f'{os.sep}a', results) + + # test it is not expanded when it is not a path + tilde_file = os.path.join(self.tempdir, '~file') + create_empty_file(tilde_file) + with change_cwd(self.tempdir): + results = glob.glob('~*', expand_tilde=True) + self.assertIn('~file', results) + @skip_unless_symlink def test_glob_symlinks(self): eq = self.assertSequencesEqual_noorder diff --git a/Misc/NEWS.d/next/Library/2025-04-20-21-12-00.gh-issue-132757.asfaeh.rst b/Misc/NEWS.d/next/Library/2025-04-20-21-12-00.gh-issue-132757.asfaeh.rst new file mode 100644 index 00000000000000..f113908e65bb3a --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-04-20-21-12-00.gh-issue-132757.asfaeh.rst @@ -0,0 +1 @@ +Implement ``~`` expansion in :func:`glob.glob`.