Skip to content

Commit d51eea6

Browse files
committed
Direct C call for os.path.splitroot()
1 parent 5689108 commit d51eea6

File tree

6 files changed

+49
-79
lines changed

6 files changed

+49
-79
lines changed

Lib/ntpath.py

Lines changed: 4 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -177,19 +177,12 @@ def splitdrive(p):
177177

178178

179179
try:
180-
from nt import _path_splitroot_ex
180+
from nt import _path_splitroot_ex as splitroot
181181
except ImportError:
182182
def splitroot(p):
183-
"""Split a pathname into drive, root and tail. The drive is defined
184-
exactly as in splitdrive(). On Windows, the root may be a single path
185-
separator or an empty string. The tail contains anything after the root.
186-
For example:
187-
188-
splitroot('//server/share/') == ('//server/share', '/', '')
189-
splitroot('C:/Users/Barney') == ('C:', '/', 'Users/Barney')
190-
splitroot('C:///spam///ham') == ('C:', '/', '//spam///ham')
191-
splitroot('Windows/notepad') == ('', '', 'Windows/notepad')
192-
"""
183+
"""Split a pathname into drive, root and tail.
184+
185+
The tail contains anything after the root."""
193186
p = os.fspath(p)
194187
if isinstance(p, bytes):
195188
sep = b'\\'
@@ -229,23 +222,6 @@ def splitroot(p):
229222
else:
230223
# Relative path, e.g. Windows
231224
return empty, empty, p
232-
else:
233-
def splitroot(p):
234-
"""Split a pathname into drive, root and tail. The drive is defined
235-
exactly as in splitdrive(). On Windows, the root may be a single path
236-
separator or an empty string. The tail contains anything after the root.
237-
For example:
238-
239-
splitroot('//server/share/') == ('//server/share', '/', '')
240-
splitroot('C:/Users/Barney') == ('C:', '/', 'Users/Barney')
241-
splitroot('C:///spam///ham') == ('C:', '/', '//spam///ham')
242-
splitroot('Windows/notepad') == ('', '', 'Windows/notepad')
243-
"""
244-
p = os.fspath(p)
245-
if isinstance(p, bytes):
246-
drive, root, tail = _path_splitroot_ex(os.fsdecode(p))
247-
return os.fsencode(drive), os.fsencode(root), os.fsencode(tail)
248-
return _path_splitroot_ex(p)
249225

250226

251227
# Split a path in head (everything up to the last '/') and tail (the

Lib/posixpath.py

Lines changed: 4 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -136,18 +136,12 @@ def splitdrive(p):
136136

137137

138138
try:
139-
from posix import _path_splitroot_ex
139+
from posix import _path_splitroot_ex as splitroot
140140
except ImportError:
141141
def splitroot(p):
142-
"""Split a pathname into drive, root and tail. On Posix, drive is always
143-
empty; the root may be empty, a single slash, or two slashes. The tail
144-
contains anything after the root. For example:
145-
146-
splitroot('foo/bar') == ('', '', 'foo/bar')
147-
splitroot('/foo/bar') == ('', '/', 'foo/bar')
148-
splitroot('//foo/bar') == ('', '//', 'foo/bar')
149-
splitroot('///foo/bar') == ('', '/', '//foo/bar')
150-
"""
142+
"""Split a pathname into drive, root and tail.
143+
144+
The tail contains anything after the root."""
151145
p = os.fspath(p)
152146
if isinstance(p, bytes):
153147
sep = b'/'
@@ -165,23 +159,6 @@ def splitroot(p):
165159
# Precisely two leading slashes, e.g.: '//foo'. Implementation defined per POSIX, see
166160
# https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap04.html#tag_04_13
167161
return empty, p[:2], p[2:]
168-
else:
169-
def splitroot(p):
170-
"""Split a pathname into drive, root and tail. On Posix, drive is always
171-
empty; the root may be empty, a single slash, or two slashes. The tail
172-
contains anything after the root. For example:
173-
174-
splitroot('foo/bar') == ('', '', 'foo/bar')
175-
splitroot('/foo/bar') == ('', '/', 'foo/bar')
176-
splitroot('//foo/bar') == ('', '//', 'foo/bar')
177-
splitroot('///foo/bar') == ('', '/', '//foo/bar')
178-
"""
179-
p = os.fspath(p)
180-
if isinstance(p, bytes):
181-
# Optimisation: the drive is always empty
182-
_, root, tail = _path_splitroot_ex(os.fsdecode(p))
183-
return b'', os.fsencode(root), os.fsencode(tail)
184-
return _path_splitroot_ex(p)
185162

186163

187164
# Return the tail (basename) part of a path, same as split(path)[1].

Lib/test/test_ntpath.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1011,6 +1011,8 @@ def test_fast_paths_in_use(self):
10111011
# There are fast paths of these functions implemented in posixmodule.c.
10121012
# Confirm that they are being used, and not the Python fallbacks in
10131013
# genericpath.py.
1014+
self.assertTrue(os.path.splitroot is nt._path_splitroot_ex)
1015+
self.assertFalse(inspect.isfunction(os.path.splitroot))
10141016
self.assertTrue(os.path.normpath is nt._path_normpath)
10151017
self.assertFalse(inspect.isfunction(os.path.normpath))
10161018
self.assertTrue(os.path.isdir is nt._path_isdir)

Lib/test/test_posixpath.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,8 @@ def test_isjunction(self):
279279
def test_fast_paths_in_use(self):
280280
# There are fast paths of these functions implemented in posixmodule.c.
281281
# Confirm that they are being used, and not the Python fallbacks
282+
self.assertTrue(os.path.splitroot is posix._path_splitroot_ex)
283+
self.assertFalse(inspect.isfunction(os.path.splitroot))
282284
self.assertTrue(os.path.normpath is posix._path_normpath)
283285
self.assertFalse(inspect.isfunction(os.path.normpath))
284286

Modules/clinic/posixmodule.c.h

Lines changed: 12 additions & 11 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Modules/posixmodule.c

Lines changed: 25 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5255,23 +5255,22 @@ os__path_islink_impl(PyObject *module, path_t *path)
52555255
/*[clinic input]
52565256
os._path_splitroot_ex
52575257
5258-
path: unicode
5258+
path: path_t(make_wide=True, nonstrict=True)
5259+
5260+
Split a pathname into drive, root and tail.
52595261
5262+
The tail contains anything after the root.
52605263
[clinic start generated code]*/
52615264

52625265
static PyObject *
5263-
os__path_splitroot_ex_impl(PyObject *module, PyObject *path)
5264-
/*[clinic end generated code: output=de97403d3dfebc40 input=f1470e12d899f9ac]*/
5266+
os__path_splitroot_ex_impl(PyObject *module, path_t *path)
5267+
/*[clinic end generated code: output=4b0072b6cdf4b611 input=6eb76e9173412c92]*/
52655268
{
5266-
Py_ssize_t len, drvsize, rootsize;
5269+
Py_ssize_t drvsize, rootsize;
52675270
PyObject *drv = NULL, *root = NULL, *tail = NULL, *result = NULL;
52685271

5269-
wchar_t *buffer = PyUnicode_AsWideCharString(path, &len);
5270-
if (!buffer) {
5271-
goto exit;
5272-
}
5273-
5274-
_Py_skiproot(buffer, len, &drvsize, &rootsize);
5272+
const wchar_t *buffer = path->wide;
5273+
_Py_skiproot(buffer, path->length, &drvsize, &rootsize);
52755274
drv = PyUnicode_FromWideChar(buffer, drvsize);
52765275
if (drv == NULL) {
52775276
goto exit;
@@ -5281,13 +5280,26 @@ os__path_splitroot_ex_impl(PyObject *module, PyObject *path)
52815280
goto exit;
52825281
}
52835282
tail = PyUnicode_FromWideChar(&buffer[drvsize + rootsize],
5284-
len - drvsize - rootsize);
5283+
path->length - drvsize - rootsize);
52855284
if (tail == NULL) {
52865285
goto exit;
52875286
}
5288-
result = Py_BuildValue("(OOO)", drv, root, tail);
5287+
if (PyBytes_Check(path->object)) {
5288+
Py_SETREF(drv, PyUnicode_EncodeFSDefault(drv));
5289+
if (drv == NULL) {
5290+
goto exit;
5291+
}
5292+
Py_SETREF(root, PyUnicode_EncodeFSDefault(root));
5293+
if (root == NULL) {
5294+
goto exit;
5295+
}
5296+
Py_SETREF(tail, PyUnicode_EncodeFSDefault(tail));
5297+
if (tail == NULL) {
5298+
goto exit;
5299+
}
5300+
}
5301+
result = PyTuple_Pack(3, drv, root, tail);
52895302
exit:
5290-
PyMem_Free(buffer);
52915303
Py_XDECREF(drv);
52925304
Py_XDECREF(root);
52935305
Py_XDECREF(tail);

0 commit comments

Comments
 (0)