Skip to content

Commit fc93c75

Browse files
Merge branch 'main' into futures-map-108518
2 parents 90b2cf8 + 9511198 commit fc93c75

40 files changed

Lines changed: 371 additions & 168 deletions

.github/workflows/reusable-check-html-ids.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,13 @@ jobs:
2020
with:
2121
persist-credentials: false
2222
ref: ${{ github.event.pull_request.head.sha }}
23+
- name: 'Downgrade Git'
24+
# Temporarily downgrade to 2.43 until 2.55 is in the runner image,
25+
# to avoid "fatal: shallow file has changed since we read it" bug.
26+
# See https://github.com/python/cpython/issues/151365.
27+
run: |
28+
sudo apt-get install -y --allow-downgrades 'git=1:2.43.*' 'git-man=1:2.43.*'
29+
git --version
2330
- name: 'Find merge base'
2431
id: merge-base
2532
run: |

.github/workflows/reusable-context.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,15 @@ jobs:
9090
|| ''
9191
}}
9292
93+
- name: 'Downgrade Git'
94+
# Temporarily downgrade to 2.43 until 2.55 is in the runner image,
95+
# to avoid "fatal: shallow file has changed since we read it" bug.
96+
# See https://github.com/python/cpython/issues/151365.
97+
if: github.event_name == 'pull_request'
98+
run: |
99+
sudo apt-get install -y --allow-downgrades 'git=1:2.43.*' 'git-man=1:2.43.*'
100+
git --version
101+
93102
# Adapted from https://github.com/actions/checkout/issues/520#issuecomment-1167205721
94103
- name: Fetch commits to get branch diff
95104
if: github.event_name == 'pull_request'

.github/workflows/reusable-docs.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,14 @@ jobs:
4747
&& github.event.pull_request.head.sha
4848
|| ''
4949
}}
50+
- name: 'Downgrade Git'
51+
# Temporarily downgrade to 2.43 until 2.55 is in the runner image,
52+
# to avoid "fatal: shallow file has changed since we read it" bug.
53+
# See https://github.com/python/cpython/issues/151365.
54+
if: github.event_name == 'pull_request'
55+
run: |
56+
sudo apt-get install -y --allow-downgrades 'git=1:2.43.*' 'git-man=1:2.43.*'
57+
git --version
5058
# Adapted from https://github.com/actions/checkout/issues/520#issuecomment-1167205721
5159
- name: 'Fetch commits to get branch diff'
5260
if: github.event_name == 'pull_request'

Doc/c-api/capsule.rst

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -108,25 +108,20 @@ Refer to :ref:`using-capsules` for more information on using these objects.
108108
109109
Import a pointer to a C object from a capsule attribute in a module. The
110110
*name* parameter should specify the full name to the attribute, as in
111-
``module.attribute``. The *name* stored in the capsule must match this
112-
string exactly.
113-
114-
This function splits *name* on the ``.`` character, and imports the first
115-
element. It then processes further elements using attribute lookups.
111+
``package.module.attribute``.
112+
Modules are imported if needed,
113+
other components are looked up as attributes.
114+
The *name* stored in the capsule must match this string exactly.
116115
117116
Return the capsule's internal *pointer* on success. On failure, set an
118117
exception and return ``NULL``.
119118
120-
.. note::
121-
122-
If *name* points to an attribute of some submodule or subpackage, this
123-
submodule or subpackage must be previously imported using other means
124-
(for example, by using :c:func:`PyImport_ImportModule`) for the
125-
attribute lookups to succeed.
126-
127119
.. versionchanged:: 3.3
128120
*no_block* has no effect anymore.
129121
122+
.. versionchanged:: next
123+
Submodules are now imported if needed.
124+
130125
131126
.. c:function:: int PyCapsule_IsValid(PyObject *capsule, const char *name)
132127

Doc/library/os.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -452,7 +452,7 @@ process and user.
452452
process. For most purposes, it is more useful to use
453453
:func:`getpass.getuser` since the latter checks the environment variables
454454
:envvar:`LOGNAME` or :envvar:`USERNAME` to find out who the user is, and
455-
falls back to ``pwd.getpwuid(os.getuid())[0]`` to get the login name of the
455+
falls back to ``pwd.getpwuid(os.getuid()).pw_name`` to get the login name of the
456456
current real user id.
457457

458458
.. availability:: Unix, Windows, not WASI.

Lib/getpass.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -428,7 +428,7 @@ def getuser():
428428

429429
try:
430430
import pwd
431-
return pwd.getpwuid(os.getuid())[0]
431+
return pwd.getpwuid(os.getuid()).pw_name
432432
except (ImportError, KeyError) as e:
433433
raise OSError('No username set in the environment') from e
434434

Lib/http/server.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -857,7 +857,7 @@ def send_head(self):
857857

858858
self.send_response(HTTPStatus.OK)
859859
self.send_header("Content-type", ctype)
860-
self.send_header("Content-Length", str(fs[6]))
860+
self.send_header("Content-Length", str(fs.st_size))
861861
self.send_header("Last-Modified",
862862
self.date_time_string(fs.st_mtime))
863863
self._send_extra_response_headers()
@@ -1013,7 +1013,7 @@ def nobody_uid():
10131013
except ImportError:
10141014
return -1
10151015
try:
1016-
nobody = pwd.getpwnam('nobody')[2]
1016+
nobody = pwd.getpwnam('nobody').pw_uid
10171017
except KeyError:
10181018
nobody = 1 + max(x[2] for x in pwd.getpwall())
10191019
return nobody

Lib/logging/config.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -934,8 +934,9 @@ def listen(port=DEFAULT_LOGGING_CONFIG_PORT, verify=None):
934934
Start up a socket server on the specified port, and listen for new
935935
configurations.
936936
937-
These will be sent as a file suitable for processing by fileConfig().
938-
Returns a Thread object on which you can call start() to start the server,
937+
These will be sent as a file suitable for processing by dictConfig() or
938+
fileConfig(). Returns a Thread object on which you can call start() to
939+
start the server,
939940
and which you can join() when appropriate. To stop the server, call
940941
stopListening().
941942
@@ -953,16 +954,16 @@ class ConfigStreamHandler(StreamRequestHandler):
953954
"""
954955
Handler for a logging configuration request.
955956
956-
It expects a completely new logging configuration and uses fileConfig
957-
to install it.
957+
It expects a completely new logging configuration and uses dictConfig
958+
or fileConfig to install it.
958959
"""
959960
def handle(self):
960961
"""
961962
Handle a request.
962963
963964
Each request is expected to be a 4-byte length, packed using
964965
struct.pack(">L", n), followed by the config file.
965-
Uses fileConfig() to do the grunt work.
966+
Uses dictConfig() or fileConfig() to do the grunt work.
966967
"""
967968
try:
968969
conn = self.connection

Lib/netrc.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def _can_security_check():
1515
def _getpwuid(uid):
1616
try:
1717
import pwd
18-
return pwd.getpwuid(uid)[0]
18+
return pwd.getpwuid(uid).pw_name
1919
except (ImportError, LookupError):
2020
return f'uid {uid}'
2121

Lib/profiling/sampling/_flamegraph_assets/flamegraph.js

Lines changed: 35 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -99,13 +99,23 @@ function getDisplayName(moduleName, filename) {
9999
return filename;
100100
}
101101

102-
function selectFlamegraphData() {
103-
const baseData = isShowingElided ? elidedFlamegraphData : normalData;
102+
function selectFlamegraphData(selectedThreadId = null) {
103+
let baseData = isShowingElided ? elidedFlamegraphData : normalData;
104+
105+
if (selectedThreadId !== null) {
106+
baseData = filterDataByThread(baseData, selectedThreadId);
107+
}
104108

105109
if (!isInverted) {
106110
return baseData;
107111
}
108112

113+
// Thread-filtered trees have different values, so invert them after filtering
114+
// instead of using the cached all-thread tree.
115+
if (selectedThreadId !== null) {
116+
return generateInvertedFlamegraph(baseData);
117+
}
118+
109119
if (isShowingElided) {
110120
if (!invertedElidedData) {
111121
invertedElidedData = generateInvertedFlamegraph(baseData);
@@ -120,12 +130,11 @@ function selectFlamegraphData() {
120130
}
121131

122132
function updateFlamegraphView() {
123-
const selectedData = selectFlamegraphData();
124133
const selectedThreadId = currentThreadFilter !== 'all' ? parseInt(currentThreadFilter, 10) : null;
125-
const filteredData = selectedThreadId !== null ? filterDataByThread(selectedData, selectedThreadId) : selectedData;
126-
const tooltip = createPythonTooltip(filteredData);
127-
const chart = createFlamegraph(tooltip, filteredData.value, filteredData);
128-
renderFlamegraph(chart, filteredData);
134+
const selectedData = selectFlamegraphData(selectedThreadId);
135+
const tooltip = createPythonTooltip(selectedData);
136+
const chart = createFlamegraph(tooltip, selectedData.value, selectedData);
137+
renderFlamegraph(chart, selectedData);
129138
populateThreadStats(selectedData, selectedThreadId);
130139
}
131140

@@ -937,7 +946,9 @@ function formatDuration(seconds) {
937946

938947
function populateProfileSummary(data) {
939948
const stats = data.stats || {};
940-
const totalSamples = stats.total_samples || data.value || 0;
949+
const totalSamples = currentThreadFilter !== 'all'
950+
? (data.value ?? 0)
951+
: (stats.total_samples ?? data.value ?? 0);
941952
const duration = stats.duration_sec || 0;
942953
const sampleRate = stats.sample_rate || (duration > 0 ? totalSamples / duration : 0);
943954
const errorRate = stats.error_rate || 0;
@@ -1209,7 +1220,7 @@ function initThreadFilter(data) {
12091220
const threadFilter = document.getElementById('thread-filter');
12101221
const threadSection = document.getElementById('thread-section');
12111222

1212-
if (!threadFilter || !data.threads) return;
1223+
if (!threadFilter || !data.threads || data.stats?.is_differential) return;
12131224

12141225
threadFilter.innerHTML = '<option value="all">All Threads</option>';
12151226

@@ -1238,11 +1249,23 @@ function filterByThread() {
12381249

12391250
function filterDataByThread(data, threadId) {
12401251
function filterNode(node) {
1241-
if (!node.threads || !node.threads.includes(threadId)) {
1252+
const threadValues = node.thread_values?.[threadId];
1253+
if (!threadValues) {
12421254
return null;
12431255
}
12441256

1245-
const filteredNode = { ...node, children: [] };
1257+
const {
1258+
thread_values: _threadValues,
1259+
thread_opcodes: threadOpcodes,
1260+
...sharedNode
1261+
} = node;
1262+
const filteredNode = {
1263+
...sharedNode,
1264+
value: threadValues[0],
1265+
self: threadValues[1],
1266+
opcodes: threadOpcodes?.[threadId] ?? {},
1267+
children: []
1268+
};
12461269

12471270
if (node.children && Array.isArray(node.children)) {
12481271
filteredNode.children = node.children
@@ -1253,25 +1276,7 @@ function filterDataByThread(data, threadId) {
12531276
return filteredNode;
12541277
}
12551278

1256-
function recalculateValue(node) {
1257-
if (!node.children || node.children.length === 0) {
1258-
return node.value || 0;
1259-
}
1260-
const childrenValue = node.children.reduce((sum, child) => sum + recalculateValue(child), 0);
1261-
node.value = Math.max(node.value || 0, childrenValue);
1262-
return node.value;
1263-
}
1264-
1265-
const filteredRoot = { ...data, children: [] };
1266-
1267-
if (data.children && Array.isArray(data.children)) {
1268-
filteredRoot.children = data.children
1269-
.map(child => filterNode(child))
1270-
.filter(child => child !== null);
1271-
}
1272-
1273-
recalculateValue(filteredRoot);
1274-
return filteredRoot;
1279+
return filterNode(data);
12751280
}
12761281

12771282
// ============================================================================

0 commit comments

Comments
 (0)