Skip to content

Commit 4c4ffc7

Browse files
Pull in main
2 parents 6e14b32 + 5e91684 commit 4c4ffc7

File tree

12 files changed

+69
-58
lines changed

12 files changed

+69
-58
lines changed

Doc/library/pathlib.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1592,6 +1592,11 @@ Copying, moving and deleting
15921592
This argument has no effect when copying files on Windows (where
15931593
metadata is always preserved).
15941594

1595+
.. note::
1596+
Where supported by the operating system and file system, this method
1597+
performs a lightweight copy, where data blocks are only copied when
1598+
modified. This is known as copy-on-write.
1599+
15951600
.. versionadded:: 3.14
15961601

15971602

Lib/pathlib/_abc.py

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ class PathGlobber(_GlobberBase):
9999
@staticmethod
100100
def concat_path(path, text):
101101
"""Appends text to the given path."""
102-
return path.with_segments(path._raw_path + text)
102+
return path.with_segments(str(path) + text)
103103

104104

105105
class PurePathBase:
@@ -112,9 +112,9 @@ class PurePathBase:
112112
"""
113113

114114
__slots__ = (
115-
# The `_raw_path` slot store a joined string path. This is set in the
116-
# `__init__()` method.
117-
'_raw_path',
115+
# The `_raw_paths` slot stores unjoined string paths. This is set in
116+
# the `__init__()` method.
117+
'_raw_paths',
118118

119119
# The '_resolving' slot stores a boolean indicating whether the path
120120
# is being processed by `PathBase.resolve()`. This prevents duplicate
@@ -124,11 +124,14 @@ class PurePathBase:
124124
parser = ParserBase()
125125
_globber = PathGlobber
126126

127-
def __init__(self, path, *paths):
128-
self._raw_path = self.parser.join(path, *paths) if paths else path
129-
if not isinstance(self._raw_path, str):
130-
raise TypeError(
131-
f"path should be a str, not {type(self._raw_path).__name__!r}")
127+
def __init__(self, arg, *args):
128+
paths = [arg]
129+
paths.extend(args)
130+
for path in paths:
131+
if not isinstance(path, str):
132+
raise TypeError(
133+
f"path should be a str, not {type(path).__name__!r}")
134+
self._raw_paths = paths
132135
self._resolving = False
133136

134137
def with_segments(self, *pathsegments):
@@ -141,7 +144,19 @@ def with_segments(self, *pathsegments):
141144
def __str__(self):
142145
"""Return the string representation of the path, suitable for
143146
passing to system calls."""
144-
return self._raw_path
147+
paths = self._raw_paths
148+
if len(paths) == 1:
149+
return paths[0]
150+
elif paths:
151+
# Join path segments from the initializer.
152+
path = self.parser.join(*paths)
153+
# Cache the joined path.
154+
paths.clear()
155+
paths.append(path)
156+
return path
157+
else:
158+
paths.append('')
159+
return ''
145160

146161
def as_posix(self):
147162
"""Return the string representation of the path with forward (/)
@@ -166,7 +181,7 @@ def anchor(self):
166181
@property
167182
def name(self):
168183
"""The final path component, if any."""
169-
return self.parser.split(self._raw_path)[1]
184+
return self.parser.split(str(self))[1]
170185

171186
@property
172187
def suffix(self):
@@ -202,7 +217,7 @@ def with_name(self, name):
202217
split = self.parser.split
203218
if split(name)[0]:
204219
raise ValueError(f"Invalid name {name!r}")
205-
return self.with_segments(split(self._raw_path)[0], name)
220+
return self.with_segments(split(str(self))[0], name)
206221

207222
def with_stem(self, stem):
208223
"""Return a new path with the stem changed."""
@@ -242,17 +257,17 @@ def relative_to(self, other, *, walk_up=False):
242257
anchor0, parts0 = self._stack
243258
anchor1, parts1 = other._stack
244259
if anchor0 != anchor1:
245-
raise ValueError(f"{self._raw_path!r} and {other._raw_path!r} have different anchors")
260+
raise ValueError(f"{str(self)!r} and {str(other)!r} have different anchors")
246261
while parts0 and parts1 and parts0[-1] == parts1[-1]:
247262
parts0.pop()
248263
parts1.pop()
249264
for part in parts1:
250265
if not part or part == '.':
251266
pass
252267
elif not walk_up:
253-
raise ValueError(f"{self._raw_path!r} is not in the subpath of {other._raw_path!r}")
268+
raise ValueError(f"{str(self)!r} is not in the subpath of {str(other)!r}")
254269
elif part == '..':
255-
raise ValueError(f"'..' segment in {other._raw_path!r} cannot be walked")
270+
raise ValueError(f"'..' segment in {str(other)!r} cannot be walked")
256271
else:
257272
parts0.append('..')
258273
return self.with_segments('', *reversed(parts0))
@@ -289,17 +304,17 @@ def joinpath(self, *pathsegments):
289304
paths) or a totally different path (if one of the arguments is
290305
anchored).
291306
"""
292-
return self.with_segments(self._raw_path, *pathsegments)
307+
return self.with_segments(*self._raw_paths, *pathsegments)
293308

294309
def __truediv__(self, key):
295310
try:
296-
return self.with_segments(self._raw_path, key)
311+
return self.with_segments(*self._raw_paths, key)
297312
except TypeError:
298313
return NotImplemented
299314

300315
def __rtruediv__(self, key):
301316
try:
302-
return self.with_segments(key, self._raw_path)
317+
return self.with_segments(key, *self._raw_paths)
303318
except TypeError:
304319
return NotImplemented
305320

@@ -311,7 +326,7 @@ def _stack(self):
311326
*parts* is a reversed list of parts following the anchor.
312327
"""
313328
split = self.parser.split
314-
path = self._raw_path
329+
path = str(self)
315330
parent, name = split(path)
316331
names = []
317332
while path != parent:
@@ -323,7 +338,7 @@ def _stack(self):
323338
@property
324339
def parent(self):
325340
"""The logical parent of the path."""
326-
path = self._raw_path
341+
path = str(self)
327342
parent = self.parser.split(path)[0]
328343
if path != parent:
329344
parent = self.with_segments(parent)
@@ -335,7 +350,7 @@ def parent(self):
335350
def parents(self):
336351
"""A sequence of this path's logical parents."""
337352
split = self.parser.split
338-
path = self._raw_path
353+
path = str(self)
339354
parent = split(path)[0]
340355
parents = []
341356
while path != parent:
@@ -347,7 +362,7 @@ def parents(self):
347362
def is_absolute(self):
348363
"""True if the path is absolute (has both a root and, if applicable,
349364
a drive)."""
350-
return self.parser.isabs(self._raw_path)
365+
return self.parser.isabs(str(self))
351366

352367
@property
353368
def _pattern_str(self):

Lib/pathlib/_local.py

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,6 @@ class PurePath(PurePathBase):
6868
"""
6969

7070
__slots__ = (
71-
# The `_raw_paths` slot stores unnormalized string paths. This is set
72-
# in the `__init__()` method.
73-
'_raw_paths',
74-
7571
# The `_drv`, `_root` and `_tail_cached` slots store parsed and
7672
# normalized parts of the path. They are set when any of the `drive`,
7773
# `root` or `_tail` properties are accessed for the first time. The
@@ -299,25 +295,14 @@ def _parse_pattern(cls, pattern):
299295
parts.append('')
300296
return parts
301297

302-
@property
303-
def _raw_path(self):
304-
"""The joined but unnormalized path."""
305-
paths = self._raw_paths
306-
if len(paths) == 0:
307-
path = ''
308-
elif len(paths) == 1:
309-
path = paths[0]
310-
else:
311-
path = self.parser.join(*paths)
312-
return path
313-
314298
@property
315299
def drive(self):
316300
"""The drive prefix (letter or UNC path), if any."""
317301
try:
318302
return self._drv
319303
except AttributeError:
320-
self._drv, self._root, self._tail_cached = self._parse_path(self._raw_path)
304+
raw_path = PurePathBase.__str__(self)
305+
self._drv, self._root, self._tail_cached = self._parse_path(raw_path)
321306
return self._drv
322307

323308
@property
@@ -326,15 +311,17 @@ def root(self):
326311
try:
327312
return self._root
328313
except AttributeError:
329-
self._drv, self._root, self._tail_cached = self._parse_path(self._raw_path)
314+
raw_path = PurePathBase.__str__(self)
315+
self._drv, self._root, self._tail_cached = self._parse_path(raw_path)
330316
return self._root
331317

332318
@property
333319
def _tail(self):
334320
try:
335321
return self._tail_cached
336322
except AttributeError:
337-
self._drv, self._root, self._tail_cached = self._parse_path(self._raw_path)
323+
raw_path = PurePathBase.__str__(self)
324+
self._drv, self._root, self._tail_cached = self._parse_path(raw_path)
338325
return self._tail_cached
339326

340327
@property

Lib/test/test_pathlib/test_pathlib_abc.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,6 @@ def test_unsupported_operation_pure(self):
8686
p.suffix
8787
with self.assertRaises(e):
8888
p.suffixes
89-
with self.assertRaises(e):
90-
p / 'bar'
91-
with self.assertRaises(e):
92-
'bar' / p
93-
self.assertRaises(e, p.joinpath, 'bar')
9489
self.assertRaises(e, p.with_name, 'bar')
9590
self.assertRaises(e, p.with_stem, 'bar')
9691
self.assertRaises(e, p.with_suffix, '.txt')
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Removed unnecessary DLLs from Windows embeddable package

Modules/_interpchannelsmodule.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2061,7 +2061,7 @@ _channel_get_info(_channels *channels, int64_t cid, struct channel_info *info)
20612061
if (interp == NULL) {
20622062
return -1;
20632063
}
2064-
Py_ssize_t interpid = PyInterpreterState_GetID(interp);
2064+
int64_t interpid = PyInterpreterState_GetID(interp);
20652065

20662066
// Hold the global lock until we're done.
20672067
PyThread_acquire_lock(channels->mutex, WAIT_LOCK);

Modules/_ssl.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4923,7 +4923,9 @@ static unsigned int psk_client_callback(SSL *s,
49234923
goto error;
49244924
}
49254925

4926-
if (identity_len_ + 1 > max_identity_len || psk_len_ > max_psk_len) {
4926+
if ((size_t)identity_len_ + 1 > max_identity_len
4927+
|| (size_t)psk_len_ > max_psk_len)
4928+
{
49274929
Py_DECREF(result);
49284930
goto error;
49294931
}
@@ -5036,7 +5038,7 @@ static unsigned int psk_server_callback(SSL *s,
50365038
goto error;
50375039
}
50385040

5039-
if (psk_len_ > max_psk_len) {
5041+
if ((size_t)psk_len_ > max_psk_len) {
50405042
Py_DECREF(result);
50415043
goto error;
50425044
}

Modules/_winapi.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2317,7 +2317,7 @@ _winapi_BatchedWaitForMultipleObjects_impl(PyObject *module,
23172317
BOOL wait_all, DWORD milliseconds)
23182318
/*[clinic end generated code: output=d21c1a4ad0a252fd input=7e196f29005dc77b]*/
23192319
{
2320-
Py_ssize_t thread_count = 0, handle_count = 0, i, j;
2320+
Py_ssize_t thread_count = 0, handle_count = 0, i;
23212321
Py_ssize_t nhandles;
23222322
BatchedWaitData *thread_data[MAXIMUM_WAIT_OBJECTS];
23232323
HANDLE handles[MAXIMUM_WAIT_OBJECTS];
@@ -2378,7 +2378,7 @@ _winapi_BatchedWaitForMultipleObjects_impl(PyObject *module,
23782378
if (data->handle_count > MAXIMUM_WAIT_OBJECTS - 1) {
23792379
data->handle_count = MAXIMUM_WAIT_OBJECTS - 1;
23802380
}
2381-
for (j = 0; j < data->handle_count; ++i, ++j) {
2381+
for (DWORD j = 0; j < data->handle_count; ++i, ++j) {
23822382
PyObject *v = PySequence_GetItem(handle_seq, i);
23832383
if (!v || !PyArg_Parse(v, F_HANDLE, &data->handles[j])) {
23842384
Py_XDECREF(v);
@@ -2526,7 +2526,7 @@ _winapi_BatchedWaitForMultipleObjects_impl(PyObject *module,
25262526
if (triggered_indices) {
25272527
for (i = 0; i < thread_count; ++i) {
25282528
Py_ssize_t triggered = (Py_ssize_t)thread_data[i]->result - WAIT_OBJECT_0;
2529-
if (triggered >= 0 && triggered < thread_data[i]->handle_count - 1) {
2529+
if (triggered >= 0 && (size_t)triggered < thread_data[i]->handle_count - 1) {
25302530
PyObject *v = PyLong_FromSsize_t(thread_data[i]->handle_base + triggered);
25312531
if (!v || PyList_Append(triggered_indices, v) < 0) {
25322532
Py_XDECREF(v);

Modules/blake2module.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,7 @@ py_blake2b_or_s_new(PyTypeObject *type, PyObject *data, int digest_size,
474474

475475
/* Validate salt parameter. */
476476
if ((salt->obj != NULL) && salt->len) {
477-
if (salt->len > (is_blake2b(self->impl) ? HACL_HASH_BLAKE2B_SALT_BYTES : HACL_HASH_BLAKE2S_SALT_BYTES)) {
477+
if ((size_t)salt->len > (is_blake2b(self->impl) ? HACL_HASH_BLAKE2B_SALT_BYTES : HACL_HASH_BLAKE2S_SALT_BYTES)) {
478478
PyErr_Format(PyExc_ValueError,
479479
"maximum salt length is %d bytes",
480480
(is_blake2b(self->impl) ? HACL_HASH_BLAKE2B_SALT_BYTES : HACL_HASH_BLAKE2S_SALT_BYTES));
@@ -485,7 +485,7 @@ py_blake2b_or_s_new(PyTypeObject *type, PyObject *data, int digest_size,
485485

486486
/* Validate personalization parameter. */
487487
if ((person->obj != NULL) && person->len) {
488-
if (person->len > (is_blake2b(self->impl) ? HACL_HASH_BLAKE2B_PERSONAL_BYTES : HACL_HASH_BLAKE2S_PERSONAL_BYTES)) {
488+
if ((size_t)person->len > (is_blake2b(self->impl) ? HACL_HASH_BLAKE2B_PERSONAL_BYTES : HACL_HASH_BLAKE2S_PERSONAL_BYTES)) {
489489
PyErr_Format(PyExc_ValueError,
490490
"maximum person length is %d bytes",
491491
(is_blake2b(self->impl) ? HACL_HASH_BLAKE2B_PERSONAL_BYTES : HACL_HASH_BLAKE2S_PERSONAL_BYTES));
@@ -534,7 +534,7 @@ py_blake2b_or_s_new(PyTypeObject *type, PyObject *data, int digest_size,
534534

535535
/* Set key length. */
536536
if ((key->obj != NULL) && key->len) {
537-
if (key->len > (is_blake2b(self->impl) ? HACL_HASH_BLAKE2B_KEY_BYTES : HACL_HASH_BLAKE2S_KEY_BYTES)) {
537+
if ((size_t)key->len > (is_blake2b(self->impl) ? HACL_HASH_BLAKE2B_KEY_BYTES : HACL_HASH_BLAKE2S_KEY_BYTES)) {
538538
PyErr_Format(PyExc_ValueError,
539539
"maximum key length is %d bytes",
540540
(is_blake2b(self->impl) ? HACL_HASH_BLAKE2B_KEY_BYTES : HACL_HASH_BLAKE2S_KEY_BYTES));

PC/layout/main.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,13 @@
3131
from .support.nuspec import *
3232

3333
TEST_PYDS_ONLY = FileStemSet("xxlimited", "xxlimited_35", "_ctypes_test", "_test*")
34+
TEST_DLLS_ONLY = set()
3435
TEST_DIRS_ONLY = FileNameSet("test", "tests")
3536

3637
IDLE_DIRS_ONLY = FileNameSet("idlelib")
3738

38-
TCLTK_PYDS_ONLY = FileStemSet("tcl*", "tk*", "_tkinter", "zlib1")
39+
TCLTK_PYDS_ONLY = FileStemSet("_tkinter")
40+
TCLTK_DLLS_ONLY = FileStemSet("tcl*", "tk*", "zlib1")
3941
TCLTK_DIRS_ONLY = FileNameSet("tkinter", "turtledemo")
4042
TCLTK_FILES_ONLY = FileNameSet("turtle.py")
4143

@@ -226,6 +228,10 @@ def in_build(f, dest="", new_name=None, no_lib=False):
226228
continue
227229
if src in EXCLUDE_FROM_DLLS:
228230
continue
231+
if src in TEST_DLLS_ONLY and not ns.include_tests:
232+
continue
233+
if src in TCLTK_DLLS_ONLY and not ns.include_tcltk:
234+
continue
229235
yield from in_build(src.name, dest=dest, no_lib=True)
230236

231237
if ns.zip_lib:

0 commit comments

Comments
 (0)