Skip to content

os.stat() on Windows succeeds for nonexistent paths with trailing spaces #84419

Closed

Description

@laloch
mannequin
BPO 40238
Nosy @pfmoore, @tjguk, @zware, @eryksun, @zooba, @gaborbernat, @laloch

Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

Show more details

GitHub fields:

assignee = None
closed_at = None
created_at = <Date 2020-04-09.14:25:30.374>
labels = ['3.8', 'type-bug', 'library', '3.9', 'OS-windows']
title = 'os.stat() on Windows succeeds for nonexistent paths with trailing spaces'
updated_at = <Date 2020-04-10.13:57:20.426>
user = 'https://github.com/laloch'

bugs.python.org fields:

activity = <Date 2020-04-10.13:57:20.426>
actor = 'eryksun'
assignee = 'none'
closed = False
closed_date = None
closer = None
components = ['Library (Lib)', 'Windows']
creation = <Date 2020-04-09.14:25:30.374>
creator = 'laloch'
dependencies = []
files = []
hgrepos = []
issue_num = 40238
keywords = []
message_count = 6.0
messages = ['366054', '366063', '366111', '366120', '366123', '366125']
nosy_count = 7.0
nosy_names = ['paul.moore', 'tim.golden', 'zach.ware', 'eryksun', 'steve.dower', 'gaborjbernat', 'laloch']
pr_nums = []
priority = 'normal'
resolution = None
stage = None
status = 'open'
superseder = None
type = 'behavior'
url = 'https://bugs.python.org/issue40238'
versions = ['Python 3.8', 'Python 3.9']

Linked PRs

Activity

  1. laloch commented on Apr 9, 2020

    lalochmannequin
    MannequinAuthor

    On Windows (Server 2012 R2 in my case) os.stat() seems to be striping significant trailing spaces off the path argument:

    >>> import os
    >>> os.stat("c:\\Program Files     ")
    os.stat_result(st_mode=16749, st_ino=281474976710717, st_dev=173025906, st_nlink=1, st_uid=0, st_gid=0, st_size=8192, st_atime=1586154685, st_mtime=1586154685, st_ctime=1377178576)
    >>> os.stat("c:\\Program Files\\     ")
    os.stat_result(st_mode=16749, st_ino=281474976710717, st_dev=173025906, st_nlink=1, st_uid=0, st_gid=0, st_size=8192, st_atime=1586154685, st_mtime=1586154685, st_ctime=1377178576)
    >>> # consequently
    >>> os.path.isdir("c:\\Program Files\\     ")
    True
    >>> os.path.isdir("c:\\Program Files     ")
    True
    >>> os.scandir("c:\\Program Files     ")
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    FileNotFoundError: [WinError 3] The system cannot find the path specified: 'c:\\Program Files     '

    The same also applies to regular files, not just directories.

  2. added
    stdlibStandard Library Python modules in the Lib/ directory
    type-bugAn unexpected behavior, bug, or error
    on Apr 9, 2020
  3. eryksun commented on Apr 9, 2020

    @eryksun
    Contributor

    os.stat() seems to be striping significant trailing spaces off
    the path argument

    The Windows file API normalizes paths to replace forward slashes with backslashes; resolve relative paths and "." and ".." components; strip trailing spaces and dots from the final component; and map reserved DOS device names in the final component of non-UNC paths to device paths (e.g. "C:/Temp/con " -> r"\\.\con").

    Use a "\\?\" extended device path to bypass normalization, e.g. r"\\?\C:\Program Files ". Because an extended path doesn't get normalized in an open or create context, it should only use backslash as the path separator and should be fully qualified, without any "." and ".." components. Also, a non-device UNC path has to explicitly use the "UNC" device, e.g. "//server/share/spam... " -> r"\\?\UNC\server\share\spam... ".

    That said, I strongly advise against using an extended path to create or rename files to use reserved DOS device names or trailing dots and spaces. Such filenames will be inaccessible by most applications.

  4. added and removed on Apr 9, 2020
  5. 18 remaining items

  6. zooba commented on May 19, 2025

    @zooba
    Member

    Ignoring trailing spaces in the hack to check extensions in stat is the only viable way (and strictly speaking they should only be ignored when the path doesn't start with a \\?\ prefix). (I considered getting the final path from the open file handle, but I'm pretty sure CreateProcess validates the path given to it rather than the resolved file, and so e.g. a symlink without .exe isn't executable even if the target is one.)

    We should avoid modifying paths that come from the OS, to ensure that users can round-trip paths perfectly. normpath is our exception, but it's also not required, and so our own functions that try to interpret paths need to handle some of the normalisation that the OS would do when actually resolving the path.

  7. added
    3.13only security fixes
    3.14bugs and security fixes
    3.16new features, bugs and security fixes
    on Aug 8, 2026
  8. serhiy-storchaka commented on Aug 8, 2026

    @serhiy-storchaka
    Member

    The difference between os.stat() and os.scandir()/os.listdir() was fixed in gh-150880: the path is now normalized before appending the wildcard, so all of them ignore trailing dots and spaces.

    What is left is the execute permissions in st_mode, which were guessed from the not normalized extension. #155392 fixes this.

  9. added 2 commits that reference this issue on Aug 29, 2026
  10. added 2 commits that reference this issue on Aug 30, 2026
  11. added a commit that references this issue on Sep 12, 2026
  12. added a commit that references this issue on Oct 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    3.13only security fixes3.14bugs and security fixes3.15bugs and security fixes3.16new features, bugs and security fixesOS-windowsstdlibStandard Library Python modules in the Lib/ directorytype-bugAn unexpected behavior, bug, or error

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions