Skip to content

gh-150557: Fix os.path.ismount() for UNC paths with no share on Windows#150572

Closed
amruthamodela06 wants to merge 1 commit into
python:mainfrom
amruthamodela06:fix-ismount-unc-no-share
Closed

gh-150557: Fix os.path.ismount() for UNC paths with no share on Windows#150572
amruthamodela06 wants to merge 1 commit into
python:mainfrom
amruthamodela06:fix-ismount-unc-no-share

Conversation

@amruthamodela06
Copy link
Copy Markdown

@amruthamodela06 amruthamodela06 commented May 29, 2026

Bug

On Windows, os.path.ismount() (ntpath.ismount()) reports a UNC path that names a server but no share as a mount point. A UNC mount point requires both a server and a share (\\server\share); a bare server name is not one.

>>> import os.path
>>> os.path.ismount(r"\\server")        # server, no share
True                                     # should be False
>>> os.path.ismount(r"\\?\UNC\server")  # extended UNC, no share
True                                     # should be False

Genuine share roots are correct and stay unchanged:

>>> os.path.ismount(r"\\server\share")
True
>>> os.path.ismount(r"\\?\UNC\server\share")
True

Regression

This is partly a regression from 3.11, where os.path.ismount(r"\\?\UNC\server") returned False. The splitroot()-based rewrite of ismount() (gh-101000) changed it to True. (That same rewrite correctly fixed \\?\UNC\server\share, which was wrongly False on 3.11.)

Root cause

ismount() treats the UNC/device branch as a mount point whenever the path equals its drive with no trailing component:

drive, root, rest = splitroot(path)
if drive and drive[0] in seps:
    return not rest

But splitroot() packs an incomplete, one-component UNC entirely into drive:

>>> ntpath.splitroot(r"\\server")
('\\server', '', '')
>>> ntpath.splitroot(r"\\?\UNC\server")
('\\?\UNC\server', '', '')

So rest is empty and the check passes despite there being no share.

Fix

Require a UNC drive to name both a server and a share - i.e. a separator with non-empty text on both sides must appear after the leading \\ (or after the \\?\UNC\ prefix for extended UNC):

if drive and drive[0] in seps:
    if rest:
        return False
    # A UNC mount point must name both a server and a share; a bare
    # server (e.g. \\server or \\?\UNC\server) is not one.
    if isinstance(drive, bytes):
        sep, altsep, unc_prefix = b'\\', b'/', b'\\\\?\\UNC\\'
    else:
        sep, altsep, unc_prefix = '\\', '/', '\\\\?\\UNC\\'
    normd = drive.replace(altsep, sep)
    if normd[:len(unc_prefix)].upper() == unc_prefix:
        start = len(unc_prefix)
    else:
        start = 2
    return start < normd.find(sep, start) < len(normd) - 1

Device paths (\\.\dev, \\?\dev), extended drive-letter paths (\\?\c:), volume GUID paths, and drive-letter roots all keep their current behavior.

Scope & safety

ismount() is a leaf function: nothing in ntpath calls it, and join() / normpath() / split() / splitdrive() are all driven by splitroot(), not ismount(). Its only stdlib consumer is pathlib.Path.is_mount(). splitroot() is not modified, so no other path operation changes. This also aligns with the existing docs, which already state that "a drive letter root and a share UNC are always mount points" - a bare server is not a share UNC.

Tests

News entry added via blurb. Regression tests added to test_ntpath.test_ismount:

  • \\server -> False
  • \\server\ (trailing separator) -> False
  • \\?\UNC\server and \\?\UNC\server\ -> False
  • bytes variants b"\\server" and b"\\?\UNC\server" -> False
  • \\server\share and \\?\UNC\server\share (str and bytes) -> still True

The existing assertions for \\localhost\c$ (with/without trailing slash, str and bytes) continue to pass. Verified locally on Windows after rebuild: test_ntpath passes, and the full test suite passes with no new failures.

Backport

The bug exists on every branch since 3.12 (where the splitroot() rewrite landed), so this looks like a backport candidate for the active maintenance branches - deferring to maintainers on exactly which.

Split off from #139916 per discussion in #150557.

@python-cla-bot
Copy link
Copy Markdown

python-cla-bot Bot commented May 29, 2026

All commit authors signed the Contributor License Agreement.

CLA signed

@amruthamodela06
Copy link
Copy Markdown
Author

The failing Windows CI checks appear unrelated to this change — test_ntpath passes on the Win32 builder. The 3 failures are in unrelated subsystems:

test_msvcrt.test_kbhit — console keyboard input
test_profiling … test_generator_not_under_consumer_arithmetic — sampling-profiler stack accuracy (reports "torn stacks", timing-sensitive)
test_cmd_line.test_python_legacy_windows_stdio — exits 0xC0000142
This PR only modifies ntpath.ismount(). Happy to rebase or re-run if that would help.

@amruthamodela06 amruthamodela06 deleted the fix-ismount-unc-no-share branch May 30, 2026 06:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant