Skip to content

Commit

Permalink
Workaround for isabs() on Windows + Python 3.13 (#652)
Browse files Browse the repository at this point in the history
Fixes #650. 

Starting from Python 3.13, `os.path.isabs("/foo")` on Windows return `False`. This causes many file operations on Windows to fail with "Permission denied". This PR makes `os.path.isabs()` on Windows behave like in Python <= 3.12.
  • Loading branch information
giampaolo authored Oct 22, 2024
1 parent 94fa520 commit 0914b22
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 4 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
strategy:
fail-fast: false
matrix:
python-version: ["3.11"]
python-version: ["3.13"]
os: [ubuntu-latest, windows-latest, macos-latest]

steps:
Expand Down
4 changes: 4 additions & 0 deletions HISTORY.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ Version: 2.1.0 - (IN DEVELOPMENT)
for local development. They can also be installed via ``pip install .[test]``
and ``pip install .[dev]``.

**Bug fixes**

* #650: file operations on Windows with Python 3.13 give "Permission denied".

Version: 2.0.0 - 2024-09-04
===========================

Expand Down
16 changes: 13 additions & 3 deletions pyftpdlib/filesystems.py
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,16 @@ def cwd(self, path):

# --- Pathname / conversion utilities

@staticmethod
def _isabs(path, _windows=os.name == "nt"):
# Windows + Python 3.13: isabs() changed so that a path
# starting with "/" is no longer considered absolute.
# https://github.com/python/cpython/issues/44626
# https://github.com/python/cpython/pull/113829/
if _windows and path.startswith("/"):
return True
return os.path.isabs(path)

def ftpnorm(self, ftppath):
"""Normalize a "virtual" ftp pathname (typically the raw string
coming from client) depending on the current working directory.
Expand All @@ -132,7 +142,7 @@ def ftpnorm(self, ftppath):
Note: directory separators are system independent ("/").
Pathname returned is always absolutized.
"""
if os.path.isabs(ftppath):
if self._isabs(ftppath):
p = os.path.normpath(ftppath)
else:
p = os.path.normpath(os.path.join(self.cwd, ftppath))
Expand All @@ -148,7 +158,7 @@ def ftpnorm(self, ftppath):
# Anti path traversal: don't trust user input, in the event
# that self.cwd is not absolute, return "/" as a safety measure.
# This is for extra protection, maybe not really necessary.
if not os.path.isabs(p):
if not self._isabs(p):
p = "/"
return p

Expand Down Expand Up @@ -185,7 +195,7 @@ def fs2ftp(self, fspath):
On invalid pathnames escaping from user's root directory
(e.g. "/home" when root is "/home/user") always return "/".
"""
if os.path.isabs(fspath):
if self._isabs(fspath):
p = os.path.normpath(fspath)
else:
p = os.path.normpath(os.path.join(self.root, fspath))
Expand Down

0 comments on commit 0914b22

Please sign in to comment.