summaryrefslogtreecommitdiffstats
path: root/Lib/pathlib.py
diff options
context:
space:
mode:
authorBarney Gale <barney.gale@gmail.com>2021-07-28 14:28:14 (GMT)
committerGitHub <noreply@github.com>2021-07-28 14:28:14 (GMT)
commit56c1f6d7edad454f382d3ecb8cdcff24ac898a50 (patch)
tree7ce6f980220018d0519934684487c737efb1306a /Lib/pathlib.py
parent531e2fbc52ce07a9cf37e0db05a5337e404dfccd (diff)
downloadcpython-56c1f6d7edad454f382d3ecb8cdcff24ac898a50.zip
cpython-56c1f6d7edad454f382d3ecb8cdcff24ac898a50.tar.gz
cpython-56c1f6d7edad454f382d3ecb8cdcff24ac898a50.tar.bz2
bpo-27827: identify a greater range of reserved filename on Windows. (GH-26698)
`pathlib.PureWindowsPath.is_reserved()` now identifies as reserved filenames with trailing spaces or colons. Co-authored-by: Barney Gale <barney.gale@foundry.com> Co-authored-by: Eryk Sun <eryksun@gmail.com>
Diffstat (limited to 'Lib/pathlib.py')
-rw-r--r--Lib/pathlib.py32
1 files changed, 21 insertions, 11 deletions
diff --git a/Lib/pathlib.py b/Lib/pathlib.py
index 8e6eb48..621fba0 100644
--- a/Lib/pathlib.py
+++ b/Lib/pathlib.py
@@ -124,16 +124,25 @@ class _WindowsFlavour(_Flavour):
ext_namespace_prefix = '\\\\?\\'
reserved_names = (
- {'CON', 'PRN', 'AUX', 'NUL'} |
- {'COM%d' % i for i in range(1, 10)} |
- {'LPT%d' % i for i in range(1, 10)}
+ {'CON', 'PRN', 'AUX', 'NUL', 'CONIN$', 'CONOUT$'} |
+ {'COM%s' % c for c in '123456789\xb9\xb2\xb3'} |
+ {'LPT%s' % c for c in '123456789\xb9\xb2\xb3'}
)
# Interesting findings about extended paths:
- # - '\\?\c:\a', '//?/c:\a' and '//?/c:/a' are all supported
- # but '\\?\c:/a' is not
- # - extended paths are always absolute; "relative" extended paths will
- # fail.
+ # * '\\?\c:\a' is an extended path, which bypasses normal Windows API
+ # path processing. Thus relative paths are not resolved and slash is not
+ # translated to backslash. It has the native NT path limit of 32767
+ # characters, but a bit less after resolving device symbolic links,
+ # such as '\??\C:' => '\Device\HarddiskVolume2'.
+ # * '\\?\c:/a' looks for a device named 'C:/a' because slash is a
+ # regular name character in the object namespace.
+ # * '\\?\c:\foo/bar' is invalid because '/' is illegal in NT filesystems.
+ # The only path separator at the filesystem level is backslash.
+ # * '//?/c:\a' and '//?/c:/a' are effectively equivalent to '\\.\c:\a' and
+ # thus limited to MAX_PATH.
+ # * Prior to Windows 8, ANSI API bytes paths are limited to MAX_PATH,
+ # even with the '\\?\' prefix.
def splitroot(self, part, sep=sep):
first = part[0:1]
@@ -195,15 +204,16 @@ class _WindowsFlavour(_Flavour):
def is_reserved(self, parts):
# NOTE: the rules for reserved names seem somewhat complicated
- # (e.g. r"..\NUL" is reserved but not r"foo\NUL").
- # We err on the side of caution and return True for paths which are
- # not considered reserved by Windows.
+ # (e.g. r"..\NUL" is reserved but not r"foo\NUL" if "foo" does not
+ # exist). We err on the side of caution and return True for paths
+ # which are not considered reserved by Windows.
if not parts:
return False
if parts[0].startswith('\\\\'):
# UNC paths are never reserved
return False
- return parts[-1].partition('.')[0].upper() in self.reserved_names
+ name = parts[-1].partition('.')[0].partition(':')[0].rstrip(' ')
+ return name.upper() in self.reserved_names
def make_uri(self, path):
# Under Windows, file URIs use the UTF-8 encoding.