aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Lib/pathlib/_abc.py
diff options
context:
space:
mode:
authorBarney Gale <barney.gale@gmail.com>2024-05-25 21:01:36 +0100
committerGitHub <noreply@github.com>2024-05-25 21:01:36 +0100
commite418fc3a6e7bade68ab5dfe72f14ddba28e6acb5 (patch)
tree67662f1eba24dd69bfef714d0a48e1274927b480 /Lib/pathlib/_abc.py
parent0c5ebe13e9937c446e9947c44f2570737ecca135 (diff)
downloadcpython-e418fc3a6e7bade68ab5dfe72f14ddba28e6acb5.tar.gz
cpython-e418fc3a6e7bade68ab5dfe72f14ddba28e6acb5.zip
GH-82805: Fix handling of single-dot file extensions in pathlib (#118952)
pathlib now treats "`.`" as a valid file extension (suffix). This brings it in line with `os.path.splitext()`. In the (private) pathlib ABCs, we add a new `ParserBase.splitext()` method that splits a path into a `(root, ext)` pair, like `os.path.splitext()`. This method is called by `PurePathBase.stem`, `suffix`, etc. In a future version of pathlib, we might make these base classes public, and so users will be able to define their own `splitext()` method to control file extension splitting. In `pathlib.PurePath` we add optimised `stem`, `suffix` and `suffixes` properties that don't use `splitext()`, which avoids computing the path base name twice.
Diffstat (limited to 'Lib/pathlib/_abc.py')
-rw-r--r--Lib/pathlib/_abc.py34
1 files changed, 16 insertions, 18 deletions
diff --git a/Lib/pathlib/_abc.py b/Lib/pathlib/_abc.py
index 3cdbb735096..6b5d9fc2a0c 100644
--- a/Lib/pathlib/_abc.py
+++ b/Lib/pathlib/_abc.py
@@ -68,6 +68,12 @@ class ParserBase:
drive. Either part may be empty."""
raise UnsupportedOperation(self._unsupported_msg('splitdrive()'))
+ def splitext(self, path):
+ """Split the path into a pair (root, ext), where *ext* is empty or
+ begins with a begins with a period and contains at most one period,
+ and *root* is everything before the extension."""
+ raise UnsupportedOperation(self._unsupported_msg('splitext()'))
+
def normcase(self, path):
"""Normalize the case of the path."""
raise UnsupportedOperation(self._unsupported_msg('normcase()'))
@@ -151,12 +157,7 @@ class PurePathBase:
This includes the leading period. For example: '.txt'
"""
- name = self.name
- i = name.rfind('.')
- if 0 < i < len(name) - 1:
- return name[i:]
- else:
- return ''
+ return self.parser.splitext(self.name)[1]
@property
def suffixes(self):
@@ -165,21 +166,18 @@ class PurePathBase:
These include the leading periods. For example: ['.tar', '.gz']
"""
- name = self.name
- if name.endswith('.'):
- return []
- name = name.lstrip('.')
- return ['.' + suffix for suffix in name.split('.')[1:]]
+ split = self.parser.splitext
+ stem, suffix = split(self.name)
+ suffixes = []
+ while suffix:
+ suffixes.append(suffix)
+ stem, suffix = split(stem)
+ return suffixes[::-1]
@property
def stem(self):
"""The final path component, minus its last suffix."""
- name = self.name
- i = name.rfind('.')
- if 0 < i < len(name) - 1:
- return name[:i]
- else:
- return name
+ return self.parser.splitext(self.name)[0]
def with_name(self, name):
"""Return a new path with the file name changed."""
@@ -208,7 +206,7 @@ class PurePathBase:
if not stem:
# If the stem is empty, we can't make the suffix non-empty.
raise ValueError(f"{self!r} has an empty name")
- elif suffix and not (suffix.startswith('.') and len(suffix) > 1):
+ elif suffix and not suffix.startswith('.'):
raise ValueError(f"Invalid suffix {suffix!r}")
else:
return self.with_name(stem + suffix)