Skip to content

Commit 23ad6d0

Browse files
authored
bpo-32556: nt._getfinalpathname, nt._getvolumepathname and nt._getdiskusage now correctly convert from bytes. (GH-5761)
1 parent 451d1ed commit 23ad6d0

File tree

4 files changed

+110
-58
lines changed

4 files changed

+110
-58
lines changed

Lib/test/test_ntpath.py

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@
77
from test import support, test_genericpath
88
from tempfile import TemporaryFile
99

10+
try:
11+
import nt
12+
except ImportError:
13+
# Most tests can complete without the nt module,
14+
# but for those that require it we import here.
15+
nt = None
1016

1117
def tester(fn, wantResult):
1218
fn = fn.replace("\\", "\\\\")
@@ -271,17 +277,9 @@ def test_expanduser(self):
271277
tester('ntpath.expanduser("~/foo/bar")',
272278
'C:\\idle\\eric/foo/bar')
273279

280+
@unittest.skipUnless(nt, "abspath requires 'nt' module")
274281
def test_abspath(self):
275-
# ntpath.abspath() can only be used on a system with the "nt" module
276-
# (reasonably), so we protect this test with "import nt". This allows
277-
# the rest of the tests for the ntpath module to be run to completion
278-
# on any platform, since most of the module is intended to be usable
279-
# from any platform.
280-
try:
281-
import nt
282-
tester('ntpath.abspath("C:\\")', "C:\\")
283-
except ImportError:
284-
self.skipTest('nt module not available')
282+
tester('ntpath.abspath("C:\\")', "C:\\")
285283

286284
def test_relpath(self):
287285
tester('ntpath.relpath("a")', 'a')
@@ -424,6 +422,34 @@ def test_ismount(self):
424422
self.assertTrue(ntpath.ismount(b"\\\\localhost\\c$"))
425423
self.assertTrue(ntpath.ismount(b"\\\\localhost\\c$\\"))
426424

425+
@unittest.skipUnless(nt, "OS helpers require 'nt' module")
426+
def test_nt_helpers(self):
427+
# Trivial validation that the helpers do not break, and support both
428+
# unicode and bytes (UTF-8) paths
429+
430+
drive, path = ntpath.splitdrive(sys.executable)
431+
drive = drive.rstrip(ntpath.sep) + ntpath.sep
432+
self.assertEqual(drive, nt._getvolumepathname(sys.executable))
433+
self.assertEqual(drive.encode(),
434+
nt._getvolumepathname(sys.executable.encode()))
435+
436+
cap, free = nt._getdiskusage(sys.exec_prefix)
437+
self.assertGreater(cap, 0)
438+
self.assertGreater(free, 0)
439+
b_cap, b_free = nt._getdiskusage(sys.exec_prefix.encode())
440+
# Free space may change, so only test the capacity is equal
441+
self.assertEqual(b_cap, cap)
442+
self.assertGreater(b_free, 0)
443+
444+
for path in [sys.prefix, sys.executable]:
445+
final_path = nt._getfinalpathname(path)
446+
self.assertIsInstance(final_path, str)
447+
self.assertGreater(len(final_path), 0)
448+
449+
b_final_path = nt._getfinalpathname(path.encode())
450+
self.assertIsInstance(b_final_path, bytes)
451+
self.assertGreater(len(b_final_path), 0)
452+
427453
class NtCommonTest(test_genericpath.CommonTest, unittest.TestCase):
428454
pathmodule = ntpath
429455
attributes = ['relpath']
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
nt._getfinalpathname, nt._getvolumepathname and nt._getdiskusage now
2+
correctly convert from bytes.

Modules/clinic/posixmodule.c.h

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -972,20 +972,23 @@ PyDoc_STRVAR(os__getfinalpathname__doc__,
972972
{"_getfinalpathname", (PyCFunction)os__getfinalpathname, METH_O, os__getfinalpathname__doc__},
973973

974974
static PyObject *
975-
os__getfinalpathname_impl(PyObject *module, PyObject *path);
975+
os__getfinalpathname_impl(PyObject *module, path_t *path);
976976

977977
static PyObject *
978978
os__getfinalpathname(PyObject *module, PyObject *arg)
979979
{
980980
PyObject *return_value = NULL;
981-
PyObject *path;
981+
path_t path = PATH_T_INITIALIZE("_getfinalpathname", "path", 0, 0);
982982

983-
if (!PyArg_Parse(arg, "U:_getfinalpathname", &path)) {
983+
if (!PyArg_Parse(arg, "O&:_getfinalpathname", path_converter, &path)) {
984984
goto exit;
985985
}
986-
return_value = os__getfinalpathname_impl(module, path);
986+
return_value = os__getfinalpathname_impl(module, &path);
987987

988988
exit:
989+
/* Cleanup for path */
990+
path_cleanup(&path);
991+
989992
return return_value;
990993
}
991994

@@ -1037,23 +1040,26 @@ PyDoc_STRVAR(os__getvolumepathname__doc__,
10371040
{"_getvolumepathname", (PyCFunction)os__getvolumepathname, METH_FASTCALL|METH_KEYWORDS, os__getvolumepathname__doc__},
10381041

10391042
static PyObject *
1040-
os__getvolumepathname_impl(PyObject *module, PyObject *path);
1043+
os__getvolumepathname_impl(PyObject *module, path_t *path);
10411044

10421045
static PyObject *
10431046
os__getvolumepathname(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
10441047
{
10451048
PyObject *return_value = NULL;
10461049
static const char * const _keywords[] = {"path", NULL};
1047-
static _PyArg_Parser _parser = {"U:_getvolumepathname", _keywords, 0};
1048-
PyObject *path;
1050+
static _PyArg_Parser _parser = {"O&:_getvolumepathname", _keywords, 0};
1051+
path_t path = PATH_T_INITIALIZE("_getvolumepathname", "path", 0, 0);
10491052

10501053
if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
1051-
&path)) {
1054+
path_converter, &path)) {
10521055
goto exit;
10531056
}
1054-
return_value = os__getvolumepathname_impl(module, path);
1057+
return_value = os__getvolumepathname_impl(module, &path);
10551058

10561059
exit:
1060+
/* Cleanup for path */
1061+
path_cleanup(&path);
1062+
10571063
return return_value;
10581064
}
10591065

@@ -5014,23 +5020,26 @@ PyDoc_STRVAR(os__getdiskusage__doc__,
50145020
{"_getdiskusage", (PyCFunction)os__getdiskusage, METH_FASTCALL|METH_KEYWORDS, os__getdiskusage__doc__},
50155021

50165022
static PyObject *
5017-
os__getdiskusage_impl(PyObject *module, Py_UNICODE *path);
5023+
os__getdiskusage_impl(PyObject *module, path_t *path);
50185024

50195025
static PyObject *
50205026
os__getdiskusage(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
50215027
{
50225028
PyObject *return_value = NULL;
50235029
static const char * const _keywords[] = {"path", NULL};
5024-
static _PyArg_Parser _parser = {"u:_getdiskusage", _keywords, 0};
5025-
Py_UNICODE *path;
5030+
static _PyArg_Parser _parser = {"O&:_getdiskusage", _keywords, 0};
5031+
path_t path = PATH_T_INITIALIZE("_getdiskusage", "path", 0, 0);
50265032

50275033
if (!_PyArg_ParseStackAndKeywords(args, nargs, kwnames, &_parser,
5028-
&path)) {
5034+
path_converter, &path)) {
50295035
goto exit;
50305036
}
5031-
return_value = os__getdiskusage_impl(module, path);
5037+
return_value = os__getdiskusage_impl(module, &path);
50325038

50335039
exit:
5040+
/* Cleanup for path */
5041+
path_cleanup(&path);
5042+
50345043
return return_value;
50355044
}
50365045

@@ -6580,4 +6589,4 @@ os_getrandom(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject
65806589
#ifndef OS_GETRANDOM_METHODDEF
65816590
#define OS_GETRANDOM_METHODDEF
65826591
#endif /* !defined(OS_GETRANDOM_METHODDEF) */
6583-
/*[clinic end generated code: output=8e5d4a01257b6292 input=a9049054013a1b77]*/
6592+
/*[clinic end generated code: output=fc603214822bdda6 input=a9049054013a1b77]*/

Modules/posixmodule.c

Lines changed: 48 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3720,29 +3720,26 @@ os__getfullpathname_impl(PyObject *module, path_t *path)
37203720
/*[clinic input]
37213721
os._getfinalpathname
37223722
3723-
path: unicode
3723+
path: path_t
37243724
/
37253725
37263726
A helper function for samepath on windows.
37273727
[clinic start generated code]*/
37283728

37293729
static PyObject *
3730-
os__getfinalpathname_impl(PyObject *module, PyObject *path)
3731-
/*[clinic end generated code: output=9bd78d0e52782e75 input=71d5e89334891bf4]*/
3730+
os__getfinalpathname_impl(PyObject *module, path_t *path)
3731+
/*[clinic end generated code: output=621a3c79bc29ebfa input=2b6b6c7cbad5fb84]*/
37323732
{
37333733
HANDLE hFile;
37343734
int buf_size;
37353735
wchar_t *target_path;
37363736
int result_length;
37373737
PyObject *result;
3738-
const wchar_t *path_wchar;
3739-
3740-
path_wchar = _PyUnicode_AsUnicode(path);
3741-
if (path_wchar == NULL)
3742-
return NULL;
3738+
const char *err = NULL;
37433739

3740+
Py_BEGIN_ALLOW_THREADS
37443741
hFile = CreateFileW(
3745-
path_wchar,
3742+
path->wide,
37463743
0, /* desired access */
37473744
0, /* share mode */
37483745
NULL, /* security attributes */
@@ -3751,32 +3748,54 @@ os__getfinalpathname_impl(PyObject *module, PyObject *path)
37513748
FILE_FLAG_BACKUP_SEMANTICS,
37523749
NULL);
37533750

3754-
if(hFile == INVALID_HANDLE_VALUE)
3755-
return win32_error_object("CreateFileW", path);
3751+
if (hFile == INVALID_HANDLE_VALUE) {
3752+
err = "CreateFileW";
3753+
goto done1;
3754+
}
37563755

37573756
/* We have a good handle to the target, use it to determine the
37583757
target path name. */
37593758
buf_size = GetFinalPathNameByHandleW(hFile, 0, 0, VOLUME_NAME_NT);
37603759

3761-
if(!buf_size)
3762-
return win32_error_object("GetFinalPathNameByHandle", path);
3760+
if (!buf_size) {
3761+
err = "GetFinalPathNameByHandle";
3762+
goto done1;
3763+
}
3764+
done1:
3765+
Py_END_ALLOW_THREADS
3766+
if (err)
3767+
return win32_error_object(err, path->object);
37633768

37643769
target_path = PyMem_New(wchar_t, buf_size+1);
37653770
if(!target_path)
37663771
return PyErr_NoMemory();
37673772

3773+
Py_BEGIN_ALLOW_THREADS
37683774
result_length = GetFinalPathNameByHandleW(hFile, target_path,
37693775
buf_size, VOLUME_NAME_DOS);
3770-
if(!result_length)
3771-
return win32_error_object("GetFinalPathNamyByHandle", path);
3776+
if (!result_length) {
3777+
err = "GetFinalPathNameByHandle";
3778+
goto done2;
3779+
}
37723780

3773-
if(!CloseHandle(hFile))
3774-
return win32_error_object("CloseHandle", path);
3781+
if (!CloseHandle(hFile)) {
3782+
err = "CloseHandle";
3783+
goto done2;
3784+
}
3785+
done2:
3786+
Py_END_ALLOW_THREADS
3787+
if (err) {
3788+
PyMem_Free(target_path);
3789+
return win32_error_object(err, path->object);
3790+
}
37753791

37763792
target_path[result_length] = 0;
37773793
result = PyUnicode_FromWideChar(target_path, result_length);
37783794
PyMem_Free(target_path);
3795+
if (path->narrow)
3796+
Py_SETREF(result, PyUnicode_EncodeFSDefault(result));
37793797
return result;
3798+
37803799
}
37813800

37823801
/*[clinic input]
@@ -3811,28 +3830,22 @@ os__isdir_impl(PyObject *module, path_t *path)
38113830
/*[clinic input]
38123831
os._getvolumepathname
38133832
3814-
path: unicode
3833+
path: path_t
38153834
38163835
A helper function for ismount on Win32.
38173836
[clinic start generated code]*/
38183837

38193838
static PyObject *
3820-
os__getvolumepathname_impl(PyObject *module, PyObject *path)
3821-
/*[clinic end generated code: output=cbdcbd1059ceef4c input=7eacadc40acbda6b]*/
3839+
os__getvolumepathname_impl(PyObject *module, path_t *path)
3840+
/*[clinic end generated code: output=804c63fd13a1330b input=722b40565fa21552]*/
38223841
{
38233842
PyObject *result;
3824-
const wchar_t *path_wchar;
38253843
wchar_t *mountpath=NULL;
38263844
size_t buflen;
38273845
BOOL ret;
38283846

3829-
path_wchar = PyUnicode_AsUnicodeAndSize(path, &buflen);
3830-
if (path_wchar == NULL)
3831-
return NULL;
3832-
buflen += 1;
3833-
38343847
/* Volume path should be shorter than entire path */
3835-
buflen = Py_MAX(buflen, MAX_PATH);
3848+
buflen = Py_MAX(path->length, MAX_PATH);
38363849

38373850
if (buflen > PY_DWORD_MAX) {
38383851
PyErr_SetString(PyExc_OverflowError, "path too long");
@@ -3844,15 +3857,17 @@ os__getvolumepathname_impl(PyObject *module, PyObject *path)
38443857
return PyErr_NoMemory();
38453858

38463859
Py_BEGIN_ALLOW_THREADS
3847-
ret = GetVolumePathNameW(path_wchar, mountpath,
3860+
ret = GetVolumePathNameW(path->wide, mountpath,
38483861
Py_SAFE_DOWNCAST(buflen, size_t, DWORD));
38493862
Py_END_ALLOW_THREADS
38503863

38513864
if (!ret) {
3852-
result = win32_error_object("_getvolumepathname", path);
3865+
result = win32_error_object("_getvolumepathname", path->object);
38533866
goto exit;
38543867
}
38553868
result = PyUnicode_FromWideChar(mountpath, wcslen(mountpath));
3869+
if (path->narrow)
3870+
Py_SETREF(result, PyUnicode_EncodeFSDefault(result));
38563871

38573872
exit:
38583873
PyMem_Free(mountpath);
@@ -9844,20 +9859,20 @@ os_statvfs_impl(PyObject *module, path_t *path)
98449859
/*[clinic input]
98459860
os._getdiskusage
98469861
9847-
path: Py_UNICODE
9862+
path: path_t
98489863
98499864
Return disk usage statistics about the given path as a (total, free) tuple.
98509865
[clinic start generated code]*/
98519866

98529867
static PyObject *
9853-
os__getdiskusage_impl(PyObject *module, Py_UNICODE *path)
9854-
/*[clinic end generated code: output=76d6adcd86b1db0b input=6458133aed893c78]*/
9868+
os__getdiskusage_impl(PyObject *module, path_t *path)
9869+
/*[clinic end generated code: output=3bd3991f5e5c5dfb input=6af8d1b7781cc042]*/
98559870
{
98569871
BOOL retval;
98579872
ULARGE_INTEGER _, total, free;
98589873

98599874
Py_BEGIN_ALLOW_THREADS
9860-
retval = GetDiskFreeSpaceExW(path, &_, &total, &free);
9875+
retval = GetDiskFreeSpaceExW(path->wide, &_, &total, &free);
98619876
Py_END_ALLOW_THREADS
98629877
if (retval == 0)
98639878
return PyErr_SetFromWindowsErr(0);

0 commit comments

Comments
 (0)