Skip to content

Commit 0606d02

Browse files
mjbommarclaude
andcommitted
gh-151788: Speed up http.server directory listing by using os.scandir
SimpleHTTPRequestHandler.list_directory() called os.path.isdir() and os.path.islink() for every entry, issuing two stat-family syscalls per file. This is wasted work on any filesystem and dominates listing time for large directories; on network filesystems such as NFS, where each call is a round-trip, it becomes severe. Use os.scandir(), whose DirEntry objects report the type from the directory read itself (d_type / READDIRPLUS), eliminating the per-entry stats in the common case and never doing more work than before. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent aa5b164 commit 0606d02

2 files changed

Lines changed: 13 additions & 7 deletions

File tree

Lib/http/server.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -872,13 +872,13 @@ def list_directory(self, path):
872872
873873
"""
874874
try:
875-
list = os.listdir(path)
875+
with os.scandir(path) as it:
876+
entries = sorted(it, key=lambda e: e.name.lower())
876877
except OSError:
877878
self.send_error(
878879
HTTPStatus.NOT_FOUND,
879880
"No permission to list directory")
880881
return None
881-
list.sort(key=lambda a: a.lower())
882882
r = []
883883
displaypath = self.path
884884
displaypath = displaypath.split('#', 1)[0]
@@ -899,14 +899,16 @@ def list_directory(self, path):
899899
r.append(f'<title>{title}</title>\n</head>')
900900
r.append(f'<body>\n<h1>{title}</h1>')
901901
r.append('<hr>\n<ul>')
902-
for name in list:
903-
fullname = os.path.join(path, name)
902+
for entry in entries:
903+
name = entry.name
904904
displayname = linkname = name
905-
# Append / for directories or @ for symbolic links
906-
if os.path.isdir(fullname):
905+
# Append / for directories or @ for symbolic links.
906+
# Use cached os.DirEntry methods to avoid a stat() per entry,
907+
# which is costly on network filesystems such as NFS.
908+
if entry.is_dir():
907909
displayname = name + "/"
908910
linkname = name + "/"
909-
if os.path.islink(fullname):
911+
if entry.is_symlink():
910912
displayname = name + "@"
911913
# Note: a link to a directory displays with @ and links with /
912914
r.append('<li><a href="%s">%s</a></li>'
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
Speed up :class:`http.server.SimpleHTTPRequestHandler` directory listings by
2+
using :func:`os.scandir` instead of :func:`os.listdir` plus an :func:`os.stat`
3+
call per entry. This is faster on all filesystems and especially on network
4+
filesystems such as NFS.

0 commit comments

Comments
 (0)