8000 Merge branch 'master' into remove-atan2-helpers-122681 · python/cpython@a4aff45 · GitHub
[go: up one dir, main page]

Skip to content

Commit a4aff45

Browse files
committed
Merge branch 'master' into remove-atan2-helpers-122681
2 parents c096fad + 8ce70d6 commit a4aff45

21 files changed

+802
-430
lines changed

Doc/library/inspect.rst

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,19 @@ attributes (see :ref:`import-mod-attrs` for module attributes):
153153
| | f_trace | tracing function for this |
154154
| | | frame, or ``None`` |
155155
+-----------------+-------------------+---------------------------+
156+
| | f_trace_lines | indicate whether a |
157+
| | | tracing event is |
158+
| | | triggered for each source |
159+
| | | source line |
160+
+-----------------+-------------------+---------------------------+
161+
| | f_trace_opcodes | indicate whether |
162+
| | | per-opcode events are |
163+
| | | requested |
164+
+-----------------+-------------------+---------------------------+
165+
| | clear() | used to clear all |
166+
| | | references to local |
167+
| | | variables |
168+
+-----------------+-------------------+---------------------------+
156169
| code | co_argcount | number of arguments (not |
157170
| | | including keyword only |
158171
| | | arguments, \* or \*\* |
@@ -214,6 +227,18 @@ attributes (see :ref:`import-mod-attrs` for module attributes):
214227
| | | arguments and local |
215228
| | | variables |
216229
+-----------------+-------------------+---------------------------+
230+
| | co_lines() | returns an iterator that |
231+
| | | yields successive |
232+
| | | bytecode ranges |
233+
+-----------------+-------------------+---------------------------+
234+
| | co_positions() | returns an iterator of |
235+
| | | source code positions for |
236+
| | | each bytecode instruction |
237+
+-----------------+-------------------+---------------------------+
238+
| | replace() | returns a copy of the |
239+
| | | code object with new |
240+
| | | values |
241+
+-----------------+-------------------+---------------------------+
217242
| generator | __name__ | name |
218243
+-----------------+-------------------+---------------------------+
219244
| | __qualname__ | qualified name |

Include/internal/pycore_parser.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,20 @@ extern "C" {
2121
struct _parser_runtime_state {
2222
#ifdef Py_DEBUG
2323
long memo_statistics[_PYPEGEN_NSTATISTICS];
24+
#ifdef Py_GIL_DISABLED
25+
PyMutex mutex;
26+
#endif
2427
#else
2528
int _not_used;
2629
#endif
2730
struct _expr dummy_name;
2831
};
2932

3033
_Py_DECLARE_STR(empty, "")
34+
#if defined(Py_DEBUG) && defined(Py_GIL_DISABLED)
3135
#define _parser_runtime_state_INIT \
3236
{ \
37+
.mutex = {0}, \
3338
.dummy_name = { \
3439
.kind = Name_kind, \
3540
.v.Name.id = &_Py_STR(empty), \
@@ -40,6 +45,20 @@ _Py_DECLARE_STR(empty, "")
4045
.end_col_offset = 0, \
4146
}, \
4247
}
48+
#else
49+
#define _parser_runtime_state_INIT \
50+
{ \
51+
.dummy_name = { \
52+
.kind = Name_kind, \
53+
.v.Name.id = &_Py_STR(empty), \
54+
.v.Name.ctx = Load, \
55+
.lineno = 1, \
56+
.col_offset = 0, \
57+
.end_lineno = 1, \
58+
.end_col_offset = 0, \
59+
}, \
60+
}
61+
#endif
4362

4463
extern struct _mod* _PyParser_ASTFromString(
4564
const char *str,

Lib/_android_support.py

Lines changed: 100 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
import io
22
import sys
3-
3+
from threading import RLock
4+
from time import sleep, time
45

56
# The maximum length of a log message in bytes, including the level marker and
6-
# tag, is defined as LOGGER_ENTRY_MAX_PAYLOAD in
7-
# platform/system/logging/liblog/include/log/log.h. As of API level 30, messages
8-
# longer than this will be be truncated by logcat. This limit has already been
9-
# reduced at least once in the history of Android (from 4076 to 4068 between API
10-
# level 23 and 26), so leave some headroom.
7+
# tag, is defined as LOGGER_ENTRY_MAX_PAYLOAD at
8+
# https://cs.android.com/android/platform/superproject/+/android-14.0.0_r1:system/logging/liblog/include/log/log.h;l=71.
9+
# Messages longer than this will be be truncated by logcat. This limit has already
10+
# been reduced at least once in the history of Android (from 4076 to 4068 between
11+
# API level 23 and 26), so leave some headroom.
1112
MAX_BYTES_PER_WRITE = 4000
1213

1314
# UTF-8 uses a maximum of 4 bytes per character, so limiting text writes to this
14-
# size ensures that TextIOWrapper can always avoid exceeding MAX_BYTES_PER_WRITE.
15+
# size ensures that we can always avoid exceeding MAX_BYTES_PER_WRITE.
1516
# However, if the actual number of bytes per character is smaller than that,
16-
# then TextIOWrapper may still join multiple consecutive text writes into binary
17+
# then we may still join multiple consecutive text writes into binary
1718
# writes containing a larger number of characters.
1819
MAX_CHARS_PER_WRITE = MAX_BYTES_PER_WRITE // 4
1920

@@ -26,18 +27,22 @@ def init_streams(android_log_write, stdout_prio, stderr_prio):
2627
if sys.executable:
2728
return # Not embedded in an app.
2829

30+
global logcat
31+
logcat = Logcat(android_log_write)
32+
2933
sys.stdout = TextLogStream(
30-
android_log_write, stdout_prio, "python.stdout", errors=sys.stdout.errors)
34+
stdout_prio, "python.stdout", errors=sys.stdout.errors)
3135
sys.stderr = TextLogStream(
32-
android_log_write, stderr_prio, "python.stderr", errors=sys.stderr.errors)
36+
stderr_prio, "python.stderr", errors=sys.stderr.errors)
3337

3438

3539
class TextLogStream(io.TextIOWrapper):
36-
def __init__(self, android_log_write, prio, tag, **kwargs):
40+
def __init__(self, prio, tag, **kwargs):
3741
kwargs.setdefault("encoding", "UTF-8")
38-
kwargs.setdefault("line_buffering", True)
39-
super().__init__(BinaryLogStream(android_log_write, prio, tag), **kwargs)
40-
self._CHUNK_SIZE = MAX_BYTES_PER_WRITE
42+
super().__init__(BinaryLogStream(prio, tag), **kwargs)
43+
self._lock = RLock()
44+
self._pending_bytes = []
45+
self._pending_bytes_count = 0
4146

4247
def __repr__(self):
4348
return f"<TextLogStream {self.buffer.tag!r}>"
@@ -52,19 +57,48 @@ def write(self, s):
5257
s = str.__str__(s)
5358

5459
# We want to emit one log message per line wherever possible, so split
55-
# the string before sending it to the superclass. Note that
56-
# "".splitlines() == [], so nothing will be logged for an empty string.
57-
for line in s.splitlines(keepends=True):
58-
while line:
59-
super().write(line[:MAX_CHARS_PER_WRITE])
60-
line = line[MAX_CHARS_PER_WRITE:]
60+
# the string into lines first. Note that "".splitlines() == [], so
61+
# nothing will be logged for an empty string.
62+
with self._lock:
63+
for line in s.splitlines(keepends=True):
64+
while line:
65+
chunk = line[:MAX_CHARS_PER_WRITE]
66+
line = line[MAX_CHARS_PER_WRITE:]
67+
self._write_chunk(chunk)
6168

6269
return len(s)
6370

71+
# The size and behavior of TextIOWrapper's buffer is not part of its public
72+
# API, so we handle buffering ourselves to avoid truncation.
73+
def _write_chunk(self, s):
74+
b = s.encode(self.encoding, self.errors)
75+
if self._pending_bytes_count + len(b) > MAX_BYTES_PER_WRITE:
76+
self.flush()
77+
78+
self._pending_bytes.append(b)
79+
self._pending_bytes_count += len(b)
80+
if (
81+
self.write_through
82+
or b.endswith(b"\n")
83+
or self._pending_bytes_count > MAX_BYTES_PER_WRITE
84+
):
85+
self.flush()
86+
87+
def flush(self):
88+
with self._lock:
89+
self.buffer.write(b"".join(self._pending_bytes))
90+
self._pending_bytes.clear()
91+
self._pending_bytes_count = 0
92+
93+
# Since this is a line-based logging system, line buffering cannot be turned
94+
# off, i.e. a newline always causes a flush.
95+
@property
96+
def line_buffering(self):
97+
return True
98+
6499

65100
class BinaryLogStream(io.RawIOBase):
66-
def __init__(self, android_log_write, prio, tag):
67-
self.android_log_write = android_log_write
101+
def __init__(self, prio, tag):
68102
self.prio = prio
69103
self.tag = tag
70104

@@ -85,10 +119,48 @@ def write(self, b):
85119

86120
# Writing an empty string to the stream should have no effect.
87121
if b:
88-
# Encode null bytes using "modified UTF-8" to avoid truncating the
89-
# message. This should not affect the return value, as the caller
90-
# may be expecting it to match the length of the input.
91-
self.android_log_write(self.prio, self.tag,
92-
b.replace(b"\x00", b"\xc0\x80"))
93-
122+
logcat.write(self.prio, self.tag, b)
94123
return len(b)
124+
125+
126+
# When a large volume of data is written to logcat at once, e.g. when a test
127+
# module fails in --verbose3 mode, there's a risk of overflowing logcat's own
128+
# buffer and losing messages. We avoid this by imposing a rate limit using the
129+
# token bucket algorithm, based on a conservative estimate of how fast `adb
130+
# logcat` can consume data.
131+
MAX_BYTES_PER_SECOND = 1024 * 1024
132+
133+
# The logcat buffer size of a device can be determined by running `logcat -g`.
134+
# We set the token bucket size to half of the buffer size of our current minimum
135+
# API level, because other things on the system will be producing messages as
136+
# well.
137+
BUCKET_SIZE = 128 * 1024
138+
139+
# https://cs.android.com/android/platform/superproject/+/android-14.0.0_r1:system/logging/liblog/include/log/log_read.h;l=39
140+
PER_MESSAGE_OVERHEAD = 28
141+
142+
143+
class Logcat:
144+
def __init__(self, android_log_write):
145+
self.android_log_write = android_log_write
146+
self._lock = RLock()
147+
self._bucket_level = 0
148+
self._prev_write_time = time()
149+
150+
def write(self, prio, tag, message):
151+
# Encode null bytes using "modified UTF-8" to avoid them truncating the
152+
# message.
153+
message = message.replace(b"\x00", b"\xc0\x80")
154+
155+
with self._lock:
156+
now = time()
157+
self._bucket_level += (
158+
(now - self._prev_write_time) * MAX_BYTES_PER_SECOND)
159+
self._bucket_level = min(self._bucket_level, BUCKET_SIZE)
160+
self._prev_write_time = now
161+
162+
self._bucket_level -= PER_MESSAGE_OVERHEAD + len(tag) + len(message)
163+
if self._bucket_level < 0:
164+
sleep(-self._bucket_level / MAX_BYTES_PER_SECOND)
165+
166+
self.android_log_write(prio, tag, message)

Lib/inspect.py

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -264,11 +264,16 @@ def isfunction(object):
264264
Function objects provide these attributes:
265265
__doc__ documentation string
266266
__name__ name with which this function was defined
267+
__qualname__ qualified name of this function
268+
__module__ name of the module the function was defined in or None
267269
__code__ code object containing compiled function bytecode
268270
__defaults__ tuple of any default values for arguments
269271
__globals__ global namespace in which this function was defined
270272
__annotations__ dict of parameter annotations
271-
__kwdefaults__ dict of keyword only parameters with defaults"""
273+
__kwdefaults__ dict of keyword only parameters with defaults
274+
__dict__ namespace which is supporting arbitrary function attributes
275+
__closure__ a tuple of cells or None
276+
__type_params__ tuple of type parameters"""
272277
return isinstance(object, types.FunctionType)
273278

274279
def _has_code_flag(f, flag):
@@ -333,17 +338,18 @@ def isgenerator(object):
333338
"""Return true if the object is a generator.
334339
335340
Generator objects provide these attributes:
336-
__iter__ defined to support iteration over container
337-
close raises a new GeneratorExit exception inside the
338-
generator to terminate the iteration
339341
gi_code code object
340342
gi_frame frame object or possibly None once the generator has
341343
been exhausted
342344
gi_running set to 1 when generator is executing, 0 otherwise
343-
next return the next item from the container
344-
send resumes the generator and "sends" a value that becomes
345+
gi_yieldfrom object being iterated by yield from or None
346+
347+
__iter__() defined to support iteration over container
348+
close() raises a new GeneratorExit exception inside the
349+
generator to terminate the iteration
350+
send() resumes the generator and "sends" a value that becomes
345351
the result of the current yield-expression
346-
throw used to raise an exception inside the generator"""
352+
throw() used to raise an exception inside the generator"""
347353
return isinstance(object, types.GeneratorType)
348354

349355
def iscoroutine(object):
@@ -378,7 +384,11 @@ def isframe(object):
378384
f_lasti index of last attempted instruction in bytecode
379385
f_lineno current line number in Python source code
380386
f_locals local namespace seen by this frame
381-
f_trace tracing function for this frame, or None"""
387+
f_trace tracing function for this frame, or None
388+
f_trace_lines is a tracing event triggered for each source line?
389+
f_trace_opcodes are per-opcode events being requested?
390+
391+
clear() used to clear all references to local variables"""
382392
return isinstance(object, types.FrameType)
383393

384394
def iscode(object):
@@ -403,7 +413,12 @@ def iscode(object):
403413
co_names tuple of names other than arguments and function locals
404414
co_nlocals number of local variables
405415
co_stacksize virtual machine stack space required
406-
co_varnames tuple of names of arguments and local variables"""
416+
co_varnames tuple of names of arguments and local variables
417+
co_qualname fully qualified function name
418+
419+
co_lines() returns an iterator that yields successive bytecode ranges
420+
co_positions() returns an iterator of source code positions for each bytecode instruction
421+
replace() returns a copy of the code object with a new values"""
407422
return isinstance(object, types.CodeType)
408423

409424
def isbuiltin(object):

Lib/re/_casefix.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Auto-generated by Tools/scripts/generate_re_casefix.py.
1+
# Auto-generated by Tools/build/generate_re_casefix.py.
22

33
# Maps the code of lowercased character to codes of different lowercased
44
# characters which have the same uppercase.

0 commit comments

Comments
 (0)
0