Strange behaviour with duplicate slashes in a file name

I just hit on some bizarre behaviour. Suppose I want to open the file C:/test/abc and do that with

open("C:/test/abc", "r")

, which works (provided the file exists).

But

open("[C://test/abc](file:///C://test/abc)", "r")

is also accepted and indeed opens the file C:/test/abc .

And even

open("[C://test////abc](file:///C://test////abc)", "r")

opens C:/test/abc
I hit on this because I tested on whether a given filename contains // and if so, I treat it as a URL. But one of my users used the above C://test//abc and so I assumed incorrectly that it was a URL.

My question: any idea why Python accepts superfluous / characters in a file name?

Because Windows is happy to have the // and does not raise an error.
You can try from cmd.exe like this:

dir “.//“

In fact linux (all unix?), macOS and windows ignore extra / without raising an error.

See this step of the path normalization, in Windows.

The path gets passed directly to C _wopen(), which calls WinAPI CreateFileW(), which calls NTAPI NtCreateFile(). In CreateFileW(), the system normalizes the path as an NT object path. Among other things, this involves replacing slashes with backslashes[1] and collapsing repeated backslashes. For example:

>>> open('C://test//abc', 'r')
Breakpoint 0 hit
ucrtbase!wopen:
00007ffe`c1454320 488bc4          mov     rax,rsp
0:000> du @rcx
00000177`7da9bb50  "C://test//abc"
0:000> g
Breakpoint 1 hit
KERNELBASE!CreateFileW:
00007ffe`c1604f80 488bc4          mov     rax,rsp
0:000> du @rcx
00000177`7da9bb50  "C://test//abc"
0:000> g
Breakpoint 2 hit
ntdll!NtCreateFile:
00007ffe`c3fef6e0 4c8bd1          mov     r10,rcx
0:000> !obja @r8
Obja +0000000000000000 at 0000005cb2deea80:
        Name is \??\C:\test\abc
        OBJ_CASE_INSENSITIVE

  1. Please do not assume that this means that the Windows API in general “perfectly” supports forward slash as a path separator in file paths. Many file and path functions in the Windows API do not support forward slash as a path separator; “\\?\” extended paths do not support forward slash as a path separator; and file paths passed on the command line often do not support forward slash as a path separator. When in doubt, use the native backslash path separator, unless you’re normalizing the path explicitly via pathlib.Path, os.path.normpath(), or os.path.abspath(). ↩︎