Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions InternalDocs/compiler.md
Original file line number Diff line number Diff line change
Expand Up @@ -359,19 +359,19 @@ in [Python/compile.c](../Python/compile.c) into a sequence of pseudo instruction
These are similar to bytecode, but in some cases they are more abstract, and are
resolved later into actual bytecode. The construction of this instruction sequence
is handled by several functions that break the task down by various AST node types.
The functions are all named `compiler_visit_{xx}` where *xx* is the name of the node
The functions are all named `codegen_visit_{xx}` where *xx* is the name of the node
type (such as `stmt`, `expr`, etc.). Each function receives a `struct compiler *`
and `{xx}_ty` where *xx* is the AST node type. Typically these functions
consist of a large 'switch' statement, branching based on the kind of
node type passed to it. Simple things are handled inline in the
'switch' statement with more complex transformations farmed out to other
functions named `compiler_{xx}` with *xx* being a descriptive name of what is
functions named `codegen_{xx}` with *xx* being a descriptive name of what is
being handled.

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

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

There are several helper functions that will emit pseudo-instructions
and are named `compiler_{xx}()` where *xx* is what the function helps
with (`list`, `boolop`, etc.). A rather useful one is `compiler_nameop()`.
and are named `codegen_{xx}()` where *xx* is what the function helps
with (`list`, `boolop`, etc.). A rather useful one is `codegen_nameop()`.
This function looks up the scope of a variable and, based on the
expression context, emits the proper opcode to load, store, or delete
the variable.
Expand Down
5 changes: 2 additions & 3 deletions Lib/asyncio/base_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -969,7 +969,7 @@ async def _sock_sendfile_native(self, sock, file, offset, count):
f"and file {file!r} combination")

async def _sock_sendfile_fallback(self, sock, file, offset, count):
if offset:
if hasattr(file, 'seek'):
file.seek(offset)
blocksize = (
min(count, constants.SENDFILE_FALLBACK_READBUFFER_SIZE)
Expand Down Expand Up @@ -1286,7 +1286,6 @@ async def sendfile(self, transport, file, offset=0, count=None,
raise RuntimeError(
f"fallback is disabled and native sendfile is not "
f"supported for transport {transport!r}")

return await self._sendfile_fallback(transport, file,
offset, count)

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

async def _sendfile_fallback(self, transp, file, offset, count):
if offset:
if hasattr(file, 'seek'):
file.seek(offset)
blocksize = min(count, 16384) if count else 16384
buf = bytearray(blocksize)
Expand Down
3 changes: 1 addition & 2 deletions Lib/asyncio/proactor_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -756,8 +756,7 @@ async def _sock_sendfile_native(self, sock, file, offset, count):
offset += blocksize
total_sent += blocksize
finally:
if total_sent > 0:
file.seek(offset)
file.seek(offset)

async def _sendfile_native(self, transp, file, offset, count):
resume_reading = transp.is_reading()
Expand Down
2 changes: 1 addition & 1 deletion Lib/asyncio/selector_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ def _test_selector_event(selector, fd, event):
class BaseSelectorEventLoop(base_events.BaseEventLoop):
"""Selector event loop.
See events.EventLoop for API specification.
See events.AbstractEventLoop for API specification.
"""

def __init__(self, selector=None):
Expand Down
18 changes: 9 additions & 9 deletions Lib/asyncio/unix_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -385,12 +385,12 @@ def _sock_sendfile_native_impl(self, fut, registered_fd, sock, fileno,
# order to simplify the common case.
self.remove_writer(registered_fd)
if fut.cancelled():
self._sock_sendfile_update_filepos(fileno, offset, total_sent)
self._sock_sendfile_update_filepos(fileno, offset)
return
if count:
blocksize = count - total_sent
if blocksize <= 0:
self._sock_sendfile_update_filepos(fileno, offset, total_sent)
self._sock_sendfile_update_filepos(fileno, offset)
fut.set_result(total_sent)
return

Expand Down Expand Up @@ -424,20 +424,20 @@ def _sock_sendfile_native_impl(self, fut, registered_fd, sock, fileno,
# plain send().
err = exceptions.SendfileNotAvailableError(
"os.sendfile call failed")
self._sock_sendfile_update_filepos(fileno, offset, total_sent)
self._sock_sendfile_update_filepos(fileno, offset)
fut.set_exception(err)
else:
self._sock_sendfile_update_filepos(fileno, offset, total_sent)
self._sock_sendfile_update_filepos(fileno, offset)
fut.set_exception(exc)
except (SystemExit, KeyboardInterrupt):
raise
except BaseException as exc:
self._sock_sendfile_update_filepos(fileno, offset, total_sent)
self._sock_sendfile_update_filepos(fileno, offset)
fut.set_exception(exc)
else:
if sent == 0:
# EOF
self._sock_sendfile_update_filepos(fileno, offset, total_sent)
self._sock_sendfile_update_filepos(fileno, offset)
fut.set_result(total_sent)
else:
offset += sent
Expand All @@ -448,9 +448,9 @@ def _sock_sendfile_native_impl(self, fut, registered_fd, sock, fileno,
fd, sock, fileno,
offset, count, blocksize, total_sent)

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

def _sock_add_cancellation_callback(self, fut, sock):
def cb(fut):
Expand Down
3 changes: 3 additions & 0 deletions Lib/asyncio/windows_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,9 @@ def sendfile(self, sock, file, offset, count):
ov = _overlapped.Overlapped(NULL)
offset_low = offset & 0xffff_ffff
offset_high = (offset >> 32) & 0xffff_ffff
# TransmitFile ignores OVERLAPPED.Offset for handles not opened with
# FILE_FLAG_OVERLAPPED, so seek the CRT file pointer to match.
file.seek(offset)
ov.TransmitFile(sock.fileno(),
msvcrt.get_osfhandle(file.fileno()),
offset_low, offset_high,
Expand Down
55 changes: 55 additions & 0 deletions Lib/test/test_asyncio/test_sendfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,61 @@ def test_sock_sendfile_zero_size(self):
self.assertEqual(ret, 0)
self.assertEqual(self.file.tell(), 0)

def check_sock_sendfile_offset(self, data, offset, force_fallback=False):
sock, proto = self.prepare_socksendfile()
with tempfile.TemporaryFile() as f:
f.write(data)
f.flush()
self.assertEqual(f.tell(), len(data))

if force_fallback:
async def _sock_sendfile_fail(sock, file, offset, count):
raise asyncio.exceptions.SendfileNotAvailableError()
with support.swap_attr(self.loop, '_sock_sendfile_native', _sock_sendfile_fail):
ret = self.run_loop(self.loop.sock_sendfile(sock, f, offset, None))
else:
ret = self.run_loop(self.loop.sock_sendfile(sock, f, offset, None))

self.assertEqual(f.tell(), len(data))

sock.close()
self.run_loop(proto.wait_closed())

self.assertEqual(ret, len(data) - offset)


def test_sock_sendfile_offset(self):
data = b'abcdef'
for offset in (0, len(data) // 2, len(data)):
for force_fallback in (False, True):
with self.subTest(offset=offset, force_fallback=force_fallback):
self.check_sock_sendfile_offset(data, offset, force_fallback)

def check_sendfile_offset(self, offset, fallback):
srv_proto, cli_proto = self.prepare_sendfile()
self.file.seek(123)
coro = self.loop.sendfile(cli_proto.transport, self.file, offset, fallback=fallback)
try:
ret = self.run_loop(coro)
except asyncio.SendfileNotAvailableError:
if fallback:
raise
cli_proto.transport.close()
self.run_loop(srv_proto.done)
return
cli_proto.transport.close()
self.run_loop(srv_proto.done)
self.assertEqual(ret, len(self.DATA) - offset)
self.assertEqual(srv_proto.nbytes, len(self.DATA) - offset)
self.assertEqual(srv_proto.data, self.DATA[offset:])
self.assertEqual(self.file.tell(), len(self.DATA))

def test_sendfile_offset(self):
for offset in (0, len(self.DATA) // 2, len(self.DATA)):
for fallback in (False, True):
with self.subTest(offset=offset, fallback=fallback):
self.check_sendfile_offset(offset, fallback)

def test_sock_sendfile_mix_with_regular_send(self):
buf = b"mix_regular_send" * (4 * 1024) # 64 KiB
sock, proto = self.prepare_socksendfile()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
:mod:`asyncio`: ``sendfile()`` and ``sock_sendfile()`` event loop methods
now call ``file.seek(offset)`` if *file* has a ``seek()`` method,
even if *offset* is ``0`` (default value).
12 changes: 9 additions & 3 deletions Python/import.c
Original file line number Diff line number Diff line change
Expand Up @@ -2168,6 +2168,7 @@ import_run_extension(PyThreadState *tstate, PyModInitFunction p0,

struct _Py_ext_module_loader_result res;
int rc = _PyImport_RunModInitFunc(p0, info, &res);
bool main_error = false;
if (rc < 0) {
/* We discard res.def. */
assert(res.module == NULL);
Expand All @@ -2192,7 +2193,8 @@ import_run_extension(PyThreadState *tstate, PyModInitFunction p0,
// obmalloc, so we create a copy here.
filename = _PyUnicode_Copy(info->filename);
if (filename == NULL) {
return NULL;
main_error = true;
goto main_finally;
}
}
else {
Expand Down Expand Up @@ -2238,6 +2240,7 @@ import_run_extension(PyThreadState *tstate, PyModInitFunction p0,
main_tstate, info->path, info->name, def, &singlephase);
if (cached == NULL) {
assert(PyErr_Occurred());
main_error = true;
goto main_finally;
}
}
Expand All @@ -2253,7 +2256,7 @@ import_run_extension(PyThreadState *tstate, PyModInitFunction p0,
// gh-144601: The exception object can't be transferred across
// interpreters. Instead, we print out an unraisable exception, and
// then raise a different exception for the calling interpreter.
if (rc < 0) {
if (rc < 0 || main_error) {
assert(PyErr_Occurred());
PyErr_FormatUnraisable("Exception while importing from subinterpreter");
}
Expand All @@ -2262,7 +2265,7 @@ import_run_extension(PyThreadState *tstate, PyModInitFunction p0,
/* Any module we got from the init function will have to be
* reloaded in the subinterpreter. */
mod = NULL;
if (rc < 0) {
if (rc < 0 || main_error) {
PyErr_SetString(PyExc_ImportError,
"failed to import from subinterpreter due to exception");
goto error;
Expand All @@ -2277,6 +2280,9 @@ import_run_extension(PyThreadState *tstate, PyModInitFunction p0,
if (rc < 0) {
goto error;
}
if (main_error) {
goto error;
}

if (res.kind == _Py_ext_module_kind_MULTIPHASE) {
assert_multiphase_def(def);
Expand Down
2 changes: 1 addition & 1 deletion Python/symtable.c
Original file line number Diff line number Diff line change
Expand Up @@ -1708,7 +1708,7 @@ symtable_enter_type_param_block(struct symtable *st, identifier name,
return 1;
}

/* VISIT, VISIT_SEQ and VIST_SEQ_TAIL take an ASDL type as their second argument.
/* VISIT, VISIT_SEQ and VISIT_SEQ_TAIL take an ASDL type as their second argument.
They use the ASDL name to synthesize the name of the C type and the visit
function.

Expand Down
Loading