Plate 95
Path.glob vs listdir+fnmatch: Localhost Lab
Hands-on Path.glob vs os.listdir+fnmatch.filter one-dir lab: real matches/s on *.py files, measured on Linux localhost today in this hands-on lab for SREs.
Aditya Challa4 min read
Intro — what this post promises
Match files in one directory: Path.glob('*.py') vs os.listdir + fnmatch.filter. This lab reports matches/s on Linux localhost.
It is not glob-vs-rglob-vs-walk (lab 76) and not fnmatch-vs-re pattern microbenches (lab 93). Focus: Path.glob vs listdir+fnmatch on a single directory.
Related links:
- glob vs rglob vs walk localhost lab
- fnmatch vs re localhost lab
- tomllib vs json localhost lab
- stat vs path stat localhost lab
- heapq merge vs sorted localhost lab
- islice vs list slice localhost lab
- dataclass replace vs manual localhost lab
- mmap write vs write localhost lab
Lab honesty (1 Oct 2026 IST): Python 3.13.5. Affiliates: 0. Flat dirs only.
Verdict up front (medium 2000 entries / 400 *.py): listdir+endswith ~721109 matches/s; listdir+fnmatch ~526570; Path.glob ~294091; Path.iterdir+suffix ~61488.
Arms
| Arm | Pattern |
|---|---|
Path.glob('*.py') | pathlib |
listdir + fnmatch.filter | classic |
listdir + endswith('.py') | fixed suffix fast path |
scandir + endswith | DirEntry |
Path.iterdir + .suffix | pathlib scan |
Lab topology
Script: lab-evidence/113-path-glob-vs-fnmatch/results/run_lab.py.
Lead table — medium (p50 matches/s)
| Arm | matches/s | entries/s |
|---|---|---|
| listdir + endswith | 721109 | 3605546 |
| scandir + endswith | 622937 | 3114687 |
| listdir + fnmatch | 526570 | 2632850 |
| Path.glob | 294091 | 1470453 |
| Path.iterdir + suffix | 61488 | 307440 |
Scale sketch (Path.glob vs listdir+fnmatch)
| Size | Path.glob | listdir+fnmatch |
|---|---|---|
| small | 295655 | 485484 |
| medium | 294091 | 526570 |
| large | 287243 | 554569 |
Glob pattern cost
*.py is a cheap filter. Patterns with multiple * / ? classes push more work into fnmatch — still usually dominated by directory listing syscall cost on large folders. Re-measure if your matcher is ** recursive (again: lab 76).
Reading it
- Simple
*.exton one dir →listdir/scandir+endswithleads here. - Need glob wildcards beyond a suffix →
fnmatchorPath.glob. Path.globstays convenient and mid-pack; fine for tooling scripts.- Recursive trees → lab 76 (
rglob/walk), not this post.
When Path.glob is still right
Tooling scripts that already speak pathlib, need brace-free relative patterns, or may later grow a single * directory segment often stay on Path.glob for clarity. The ~2× gap vs listdir+fnmatch on medium here rarely matters unless you scan directories in a tight loop.
endswith vs fnmatch
endswith(".py") is not a glob. It misses patterns like test_*.py or *.pyc exclusions. Use it only for fixed-suffix filters; otherwise fnmatch.filter / Path.glob keep semantics honest. Lab 93 covers pattern-engine speed in isolation — not directory IO.
Object tax
Path.iterdir() yielding Path objects paid a clear tax versus name-only listdir/scandir arms. If you only need filenames for a filter, keep strings until you must touch the filesystem again.
Pitfalls
- Using
Path.iterdirlists when you only need names (Path object tax showed up here). - Running
rglobaccidentally when one directory was intended. - Treating fnmatch pattern speed (lab 93) as directory listing speed.
- Ignoring locale/case rules on case-insensitive filesystems.
Reproduce
Evidence: summary.json, summary.txt, tree/.
For CI file collectors, scandir+endswith on a fixed suffix is a good default; graduate to fnmatch/Path.glob when patterns get expressive.
Always verify match counts against a known fixture before trusting a faster filter in production sweeps.
Limits
One Linux box. Flat synthetic directories. Pattern fixed to *.py. Not networked FS.
Takeaway
At 2000 entries, listdir+fnmatch ~526570 matches/s beat Path.glob ~294091; plain endswith ~721109 led for suffix-only filters. Use Path.glob for ergonomics; listdir+fnmatch/endswith when directory scans are hot.
Lab evidence
What I found running this
Lab 1 Oct 2026 IST. Python 3.13.5. medium 2000/400: listdir_fnmatch 526570 matches/s; Path.glob 294091; endswith 721109. One dir only — not lab 76/93. Affiliates: 0. Evidence: lab-evidence/113-path-glob-vs-fnmatch/.
Related links
Plate 58
os.stat vs Path.stat vs lstat: Localhost Lab
Hands-on os.stat vs pathlib.Path.stat vs os.lstat: real metadata stats/s on many regular files, measured on Linux localhost in this hands-on lab for SREs.
Observability & SRE · 30 Sept 2026
Plate 18
fnmatch vs re Name Filter: Localhost Lab
Hands-on fnmatch.filter vs re.compile name-list filtering measured on Linux localhost.
Observability & SRE · 30 Sept 2026
Plate 85
scandir vs listdir vs iterdir: Localhost Lab
Hands-on os.scandir vs listdir vs Path.iterdir lab: real entries/s for names and is_file on a synthetic tree, measured on Linux localhost (lab) for SREs.
Observability & SRE · 30 Sept 2026