Skip to content

Commit e6fd6d8

Browse files
authored
Merge branch 'main' into jit-wide-int-fastpath
2 parents 4d49566 + 5535c1f commit e6fd6d8

11 files changed

Lines changed: 95 additions & 25 deletions

File tree

Doc/library/pydoc.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@ will start a HTTP server on port 1234, allowing you to browse the
6868
documentation at ``http://localhost:1234/`` in your preferred web browser.
6969
Specifying ``0`` as the port number will select an arbitrary unused port.
7070

71+
.. warning::
72+
73+
The :mod:`!pydoc` HTTP server is intended for local use during
74+
development and is not suitable for production use.
75+
7176
:program:`python -m pydoc -n <hostname>` will start the server listening at the given
7277
hostname. By default the hostname is 'localhost' but if you want the server to
7378
be reached from other machines, you may want to change the host name that the

InternalDocs/compiler.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -359,19 +359,19 @@ in [Python/compile.c](../Python/compile.c) into a sequence of pseudo instruction
359359
These are similar to bytecode, but in some cases they are more abstract, and are
360360
resolved later into actual bytecode. The construction of this instruction sequence
361361
is handled by several functions that break the task down by various AST node types.
362-
The functions are all named `compiler_visit_{xx}` where *xx* is the name of the node
362+
The functions are all named `codegen_visit_{xx}` where *xx* is the name of the node
363363
type (such as `stmt`, `expr`, etc.). Each function receives a `struct compiler *`
364364
and `{xx}_ty` where *xx* is the AST node type. Typically these functions
365365
consist of a large 'switch' statement, branching based on the kind of
366366
node type passed to it. Simple things are handled inline in the
367367
'switch' statement with more complex transformations farmed out to other
368-
functions named `compiler_{xx}` with *xx* being a descriptive name of what is
368+
functions named `codegen_{xx}` with *xx* being a descriptive name of what is
369369
being handled.
370370

371371
When transforming an arbitrary AST node, use the `VISIT()` macro.
372-
The appropriate `compiler_visit_{xx}` function is called, based on the value
372+
The appropriate `codegen_visit_{xx}` function is called, based on the value
373373
passed in for <node type> (so `VISIT({c}, expr, {node})` calls
374-
`compiler_visit_expr({c}, {node})`). The `VISIT_SEQ()` macro is very similar,
374+
`codegen_visit_expr({c}, {node})`). The `VISIT_SEQ()` macro is very similar,
375375
but is called on AST node sequences (those values that were created as
376376
arguments to a node that used the '*' modifier).
377377

@@ -414,8 +414,8 @@ which is added at the end of a function is not associated with any
414414
line in the source code.
415415

416416
There are several helper functions that will emit pseudo-instructions
417-
and are named `compiler_{xx}()` where *xx* is what the function helps
418-
with (`list`, `boolop`, etc.). A rather useful one is `compiler_nameop()`.
417+
and are named `codegen_{xx}()` where *xx* is what the function helps
418+
with (`list`, `boolop`, etc.). A rather useful one is `codegen_nameop()`.
419419
This function looks up the scope of a variable and, based on the
420420
expression context, emits the proper opcode to load, store, or delete
421421
the variable.

Lib/asyncio/base_events.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -969,7 +969,7 @@ async def _sock_sendfile_native(self, sock, file, offset, count):
969969
f"and file {file!r} combination")
970970

971971
async def _sock_sendfile_fallback(self, sock, file, offset, count):
972-
if offset:
972+
if hasattr(file, 'seek'):
973973
file.seek(offset)
974974
blocksize = (
975975
min(count, constants.SENDFILE_FALLBACK_READBUFFER_SIZE)
@@ -1286,7 +1286,6 @@ async def sendfile(self, transport, file, offset=0, count=None,
12861286
raise RuntimeError(
12871287
f"fallback is disabled and native sendfile is not "
12881288
f"supported for transport {transport!r}")
1289-
12901289
return await self._sendfile_fallback(transport, file,
12911290
offset, count)
12921291

@@ -1295,7 +1294,7 @@ async def _sendfile_native(self, transp, file, offset, count):
12951294
"sendfile syscall is not supported")
12961295

12971296
async def _sendfile_fallback(self, transp, file, offset, count):
1298-
if offset:
1297+
if hasattr(file, 'seek'):
12991298
file.seek(offset)
13001299
blocksize = min(count, 16384) if count else 16384
13011300
buf = bytearray(blocksize)

Lib/asyncio/proactor_events.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -756,8 +756,7 @@ async def _sock_sendfile_native(self, sock, file, offset, count):
756756
offset += blocksize
757757
total_sent += blocksize
758758
finally:
759-
if total_sent > 0:
760-
file.seek(offset)
759+
file.seek(offset)
761760

762761
async def _sendfile_native(self, transp, file, offset, count):
763762
resume_reading = transp.is_reading()

Lib/asyncio/selector_events.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ def _test_selector_event(selector, fd, event):
5353
class BaseSelectorEventLoop(base_events.BaseEventLoop):
5454
"""Selector event loop.
5555
56-
See events.EventLoop for API specification.
56+
See events.AbstractEventLoop for API specification.
5757
"""
5858

5959
def __init__(self, selector=None):

Lib/asyncio/unix_events.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -385,12 +385,12 @@ def _sock_sendfile_native_impl(self, fut, registered_fd, sock, fileno,
385385
# order to simplify the common case.
386386
self.remove_writer(registered_fd)
387387
if fut.cancelled():
388-
self._sock_sendfile_update_filepos(fileno, offset, total_sent)
388+
self._sock_sendfile_update_filepos(fileno, offset)
389389
return
390390
if count:
391391
blocksize = count - total_sent
392392
if blocksize <= 0:
393-
self._sock_sendfile_update_filepos(fileno, offset, total_sent)
393+
self._sock_sendfile_update_filepos(fileno, offset)
394394
fut.set_result(total_sent)
395395
return
396396

@@ -424,20 +424,20 @@ def _sock_sendfile_native_impl(self, fut, registered_fd, sock, fileno,
424424
# plain send().
425425
err = exceptions.SendfileNotAvailableError(
426426
"os.sendfile call failed")
427-
self._sock_sendfile_update_filepos(fileno, offset, total_sent)
427+
self._sock_sendfile_update_filepos(fileno, offset)
428428
fut.set_exception(err)
429429
else:
430-
self._sock_sendfile_update_filepos(fileno, offset, total_sent)
430+
self._sock_sendfile_update_filepos(fileno, offset)
431431
fut.set_exception(exc)
432432
except (SystemExit, KeyboardInterrupt):
433433
raise
434434
except BaseException as exc:
435-
self._sock_sendfile_update_filepos(fileno, offset, total_sent)
435+
self._sock_sendfile_update_filepos(fileno, offset)
436436
fut.set_exception(exc)
437437
else:
438438
if sent == 0:
439439
# EOF
440-
self._sock_sendfile_update_filepos(fileno, offset, total_sent)
440+
self._sock_sendfile_update_filepos(fileno, offset)
441441
fut.set_result(total_sent)
442442
else:
443443
offset += sent
@@ -448,9 +448,9 @@ def _sock_sendfile_native_impl(self, fut, registered_fd, sock, fileno,
448448
fd, sock, fileno,
449449
offset, count, blocksize, total_sent)
450450

451-
def _sock_sendfile_update_filepos(self, fileno, offset, total_sent):
452-
if total_sent > 0:
453-
os.lseek(fileno, offset, os.SEEK_SET)
451+
def _sock_sendfile_update_filepos(self, fileno, offset):
452+
# After this helper runs, the source fd's lseek pointer is at offset."
453+
os.lseek(fileno, offset, os.SEEK_SET)
454454

455455
def _sock_add_cancellation_callback(self, fut, sock):
456456
def cb(fut):

Lib/asyncio/windows_events.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,9 @@ def sendfile(self, sock, file, offset, count):
610610
ov = _overlapped.Overlapped(NULL)
611611
offset_low = offset & 0xffff_ffff
612612
offset_high = (offset >> 32) & 0xffff_ffff
613+
# TransmitFile ignores OVERLAPPED.Offset for handles not opened with
614+
# FILE_FLAG_OVERLAPPED, so seek the CRT file pointer to match.
615+
file.seek(offset)
613616
ov.TransmitFile(sock.fileno(),
614617
msvcrt.get_osfhandle(file.fileno()),
615618
offset_low, offset_high,

Lib/test/test_asyncio/test_sendfile.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,61 @@ def test_sock_sendfile_zero_size(self):
228228
self.assertEqual(ret, 0)
229229
self.assertEqual(self.file.tell(), 0)
230230

231+
def check_sock_sendfile_offset(self, data, offset, force_fallback=False):
232+
sock, proto = self.prepare_socksendfile()
233+
with tempfile.TemporaryFile() as f:
234+
f.write(data)
235+
f.flush()
236+
self.assertEqual(f.tell(), len(data))
237+
238+
if force_fallback:
239+
async def _sock_sendfile_fail(sock, file, offset, count):
240+
raise asyncio.exceptions.SendfileNotAvailableError()
241+
with support.swap_attr(self.loop, '_sock_sendfile_native', _sock_sendfile_fail):
242+
ret = self.run_loop(self.loop.sock_sendfile(sock, f, offset, None))
243+
else:
244+
ret = self.run_loop(self.loop.sock_sendfile(sock, f, offset, None))
245+
246+
self.assertEqual(f.tell(), len(data))
247+
248+
sock.close()
249+
self.run_loop(proto.wait_closed())
250+
251+
self.assertEqual(ret, len(data) - offset)
252+
253+
254+
def test_sock_sendfile_offset(self):
255+
data = b'abcdef'
256+
for offset in (0, len(data) // 2, len(data)):
257+
for force_fallback in (False, True):
258+
with self.subTest(offset=offset, force_fallback=force_fallback):
259+
self.check_sock_sendfile_offset(data, offset, force_fallback)
260+
261+
def check_sendfile_offset(self, offset, fallback):
262+
srv_proto, cli_proto = self.prepare_sendfile()
263+
self.file.seek(123)
264+
coro = self.loop.sendfile(cli_proto.transport, self.file, offset, fallback=fallback)
265+
try:
266+
ret = self.run_loop(coro)
267+
except asyncio.SendfileNotAvailableError:
268+
if fallback:
269+
raise
270+
cli_proto.transport.close()
271+
self.run_loop(srv_proto.done)
272+
return
273+
cli_proto.transport.close()
274+
self.run_loop(srv_proto.done)
275+
self.assertEqual(ret, len(self.DATA) - offset)
276+
self.assertEqual(srv_proto.nbytes, len(self.DATA) - offset)
277+
self.assertEqual(srv_proto.data, self.DATA[offset:])
278+
self.assertEqual(self.file.tell(), len(self.DATA))
279+
280+
def test_sendfile_offset(self):
281+
for offset in (0, len(self.DATA) // 2, len(self.DATA)):
282+
for fallback in (False, True):
283+
with self.subTest(offset=offset, fallback=fallback):
284+
self.check_sendfile_offset(offset, fallback)
285+
231286
def test_sock_sendfile_mix_with_regular_send(self):
232287
buf = b"mix_regular_send" * (4 * 1024) # 64 KiB
233288
sock, proto = self.prepare_socksendfile()
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
:mod:`asyncio`: ``sendfile()`` and ``sock_sendfile()`` event loop methods
2+
now call ``file.seek(offset)`` if *file* has a ``seek()`` method,
3+
even if *offset* is ``0`` (default value).

Python/import.c

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2168,6 +2168,7 @@ import_run_extension(PyThreadState *tstate, PyModInitFunction p0,
21682168

21692169
struct _Py_ext_module_loader_result res;
21702170
int rc = _PyImport_RunModInitFunc(p0, info, &res);
2171+
bool main_error = false;
21712172
if (rc < 0) {
21722173
/* We discard res.def. */
21732174
assert(res.module == NULL);
@@ -2192,7 +2193,8 @@ import_run_extension(PyThreadState *tstate, PyModInitFunction p0,
21922193
// obmalloc, so we create a copy here.
21932194
filename = _PyUnicode_Copy(info->filename);
21942195
if (filename == NULL) {
2195-
return NULL;
2196+
main_error = true;
2197+
goto main_finally;
21962198
}
21972199
}
21982200
else {
@@ -2238,6 +2240,7 @@ import_run_extension(PyThreadState *tstate, PyModInitFunction p0,
22382240
main_tstate, info->path, info->name, def, &singlephase);
22392241
if (cached == NULL) {
22402242
assert(PyErr_Occurred());
2243+
main_error = true;
22412244
goto main_finally;
22422245
}
22432246
}
@@ -2253,7 +2256,7 @@ import_run_extension(PyThreadState *tstate, PyModInitFunction p0,
22532256
// gh-144601: The exception object can't be transferred across
22542257
// interpreters. Instead, we print out an unraisable exception, and
22552258
// then raise a different exception for the calling interpreter.
2256-
if (rc < 0) {
2259+
if (rc < 0 || main_error) {
22572260
assert(PyErr_Occurred());
22582261
PyErr_FormatUnraisable("Exception while importing from subinterpreter");
22592262
}
@@ -2262,7 +2265,7 @@ import_run_extension(PyThreadState *tstate, PyModInitFunction p0,
22622265
/* Any module we got from the init function will have to be
22632266
* reloaded in the subinterpreter. */
22642267
mod = NULL;
2265-
if (rc < 0) {
2268+
if (rc < 0 || main_error) {
22662269
PyErr_SetString(PyExc_ImportError,
22672270
"failed to import from subinterpreter due to exception");
22682271
goto error;
@@ -2277,6 +2280,9 @@ import_run_extension(PyThreadState *tstate, PyModInitFunction p0,
22772280
if (rc < 0) {
22782281
goto error;
22792282
}
2283+
if (main_error) {
2284+
goto error;
2285+
}
22802286

22812287
if (res.kind == _Py_ext_module_kind_MULTIPHASE) {
22822288
assert_multiphase_def(def);

0 commit comments

Comments
 (0)