Skip to main content

Lib/glob.py

cpython 3.14 @ ab2d84fe1023/Lib/glob.py

Lib/glob.py implements glob() and iglob(), which expand Unix shell wildcard patterns against the filesystem. Patterns can contain * (any filename segment), ** (recursive directory walk), ? (single character), and [...] (character class). The implementation uses os.scandir for directory listing and fnmatch.filter for pattern matching.

Map

LinesSymbolRole
1-30imports, __all__Module setup
31-80glob, iglobPublic API; iglob is a generator, glob materializes it
81-140_iglobCore generator; splits pattern on os.sep and recurses
141-200_iterdir, _iterdir_dirsos.scandir-based directory listing with DirEntry caching
201-250_glob1, _glob0Single-segment match with fnmatch.filter
251-300_rlistdirRecursive ** walk yielding all descendant directories

Reading

Pattern splitting and recursion

_iglob splits the pattern on os.sep into a head (directory prefix) and tail (remaining pattern). It handles the three cases: no magic characters (literal path test), ** (recursive walk via _rlistdir), and single-segment with wildcards (_glob1).

# CPython: Lib/glob.py:90 _iglob
def _iglob(pathname, recursive, dironly):
dirname, basename = os.path.split(pathname)
if not has_magic(pathname):
...
yield pathname
return
if _isrecursive(basename):
yield from _rlistdir(dirname, dironly)
return
dirs = _iglob(dirname, recursive, True) if has_magic(dirname) else [dirname]
for dirname in dirs:
for name in _glob1(dirname, basename, dironly):
yield os.path.join(dirname, name)

_glob1: fnmatch filtering

_glob1 lists the directory with os.scandir (filtering to dirs-only when dironly is True) and applies fnmatch.filter to the resulting names.

# CPython: Lib/glob.py:170 _glob1
def _glob1(dirname, pattern, dironly):
if not dirname:
dirname = os.curdir
try:
names = _iterdir(dirname, dironly)
except OSError:
return
if not _ishidden(pattern):
names = (x for x in names if not _ishidden(x))
yield from fnmatch.filter(names, pattern)

_rlistdir: recursive ** expansion

_rlistdir is a depth-first recursive generator that yields the starting directory and all descendants. It is used when ** appears in the pattern.

# CPython: Lib/glob.py:255 _rlistdir
def _rlistdir(dirname, dironly):
yield dirname
for x in _iterdir_dirs(dirname):
path = os.path.join(dirname, x) if dirname else x
yield from _rlistdir(path, dironly)

gopy notes

Not yet ported. The Go standard library's filepath.Glob provides basic * and ? support. A full port at module/glob/ would need to implement ** recursion and the [...] character class via filepath.Match or a custom walker.

CPython 3.14 changes

3.14 refactored glob into a Glob class (PEP 428 / pathlib unification) that backs both glob.glob() and pathlib.Path.glob(). The old functional API is preserved as a compatibility shim. iglob now accepts a root_dir keyword argument that sets the starting directory without changing the pattern.