Merge branch 'main' into doctest-displayhook-fix-70280

This commit is contained in:
Sergey B Kirpichev 2024-05-14 09:40:36 +03:00 committed by GitHub
commit 42b7031e52
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
1328 changed files with 71008 additions and 28578 deletions

View file

@ -1,4 +1,4 @@
FROM docker.io/library/fedora:37
FROM docker.io/library/fedora:40
ENV CC=clang

12
.github/CODEOWNERS vendored
View file

@ -243,6 +243,18 @@ Lib/test/support/interpreters/ @ericsnowcurrently
Modules/_xx*interp*module.c @ericsnowcurrently
Lib/test/test_interpreters/ @ericsnowcurrently
# Android
**/*Android* @mhsmith
**/*android* @mhsmith
# iOS (but not termios)
**/iOS* @freakboy3742
**/ios* @freakboy3742
**/*_iOS* @freakboy3742
**/*_ios* @freakboy3742
**/*-iOS* @freakboy3742
**/*-ios* @freakboy3742
# WebAssembly
/Tools/wasm/ @brettcannon

View file

@ -8,19 +8,11 @@ on:
push:
branches:
- 'main'
- '3.12'
- '3.11'
- '3.10'
- '3.9'
- '3.8'
- '3.*'
pull_request:
branches:
- 'main'
- '3.12'
- '3.11'
- '3.10'
- '3.9'
- '3.8'
- '3.*'
permissions:
contents: read
@ -137,6 +129,7 @@ jobs:
uses: actions/cache@v4
with:
path: config.cache
# Include env.pythonLocation in key to avoid changes in environment when setup-python updates Python
key: ${{ github.job }}-${{ runner.os }}-${{ env.IMAGE_VERSION }}-${{ needs.check_source.outputs.config_hash }}-${{ env.pythonLocation }}
- name: Install Dependencies
run: sudo ./.github/workflows/posix-deps-apt.sh
@ -217,7 +210,7 @@ jobs:
with:
config_hash: ${{ needs.check_source.outputs.config_hash }}
free-threading: true
# macos-14 is M1
# macos-14-large is Intel with 12 cores (most parallelism)
os-matrix: '["macos-14"]'
build_ubuntu:
@ -249,7 +242,7 @@ jobs:
build_ubuntu_ssltests:
name: 'Ubuntu SSL tests with OpenSSL'
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
timeout-minutes: 60
needs: check_source
if: needs.check_source.outputs.run_tests == 'true'
@ -315,7 +308,7 @@ jobs:
test_hypothesis:
name: "Hypothesis tests on Ubuntu"
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
timeout-minutes: 60
needs: check_source
if: needs.check_source.outputs.run_tests == 'true' && needs.check_source.outputs.run_hypothesis == 'true'
@ -428,7 +421,7 @@ jobs:
build_asan:
name: 'Address sanitizer'
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
timeout-minutes: 60
needs: check_source
if: needs.check_source.outputs.run_tests == 'true'
@ -492,6 +485,8 @@ jobs:
with:
config_hash: ${{ needs.check_source.outputs.config_hash }}
options: ./configure --config-cache --with-thread-sanitizer --with-pydebug
suppressions_path: Tools/tsan/supressions.txt
tsan_logs_artifact_name: tsan-logs-default
build_tsan_free_threading:
name: 'Thread sanitizer (free-threading)'
@ -501,6 +496,8 @@ jobs:
with:
config_hash: ${{ needs.check_source.outputs.config_hash }}
options: ./configure --config-cache --disable-gil --with-thread-sanitizer --with-pydebug
suppressions_path: Tools/tsan/suppressions_free_threading.txt
tsan_logs_artifact_name: tsan-logs-free-threading
# CIFuzz job based on https://google.github.io/oss-fuzz/getting-started/continuous-integration/
cifuzz:
@ -514,8 +511,7 @@ jobs:
strategy:
fail-fast: false
matrix:
# sanitizer: [address, undefined, memory] -- memory skipped temporarily until GH-116886 is solved.
sanitizer: [address, undefined]
sanitizer: [address, undefined, memory]
steps:
- name: Build fuzzers (${{ matrix.sanitizer }})
id: build

View file

@ -5,15 +5,16 @@ on:
- '**jit**'
- 'Python/bytecodes.c'
- 'Python/optimizer*.c'
- 'Python/optimizer_bytecodes.c'
push:
paths:
- '**jit**'
- 'Python/bytecodes.c'
- 'Python/optimizer*.c'
- 'Python/optimizer_bytecodes.c'
workflow_dispatch:
permissions:
contents: read
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
@ -22,7 +23,7 @@ jobs:
jit:
name: ${{ matrix.target }} (${{ matrix.debug && 'Debug' || 'Release' }})
runs-on: ${{ matrix.runner }}
timeout-minutes: 60
timeout-minutes: 90
strategy:
fail-fast: false
matrix:
@ -40,7 +41,7 @@ jobs:
- true
- false
llvm:
- 16
- 18
include:
- target: i686-pc-windows-msvc/msvc
architecture: Win32
@ -74,14 +75,10 @@ jobs:
architecture: aarch64
runner: ubuntu-latest
compiler: gcc
# These fail because of emulation, not because of the JIT:
exclude: test_unix_events test_init test_process_pool test_shutdown test_multiprocessing_fork test_cmd_line test_faulthandler test_os test_perf_profiler test_posix test_signal test_socket test_subprocess test_threading test_venv test_external_inspection
- target: aarch64-unknown-linux-gnu/clang
architecture: aarch64
runner: ubuntu-latest
compiler: clang
# These fail because of emulation, not because of the JIT:
exclude: test_unix_events test_init test_process_pool test_shutdown test_multiprocessing_fork test_cmd_line test_faulthandler test_os test_perf_profiler test_posix test_signal test_socket test_subprocess test_threading test_venv test_external_inspection
env:
CC: ${{ matrix.compiler }}
steps:
@ -93,37 +90,43 @@ jobs:
- name: Native Windows
if: runner.os == 'Windows' && matrix.architecture != 'ARM64'
run: |
choco upgrade llvm -y
choco install llvm --allow-downgrade --no-progress --version ${{ matrix.llvm }}
./PCbuild/build.bat --experimental-jit ${{ matrix.debug && '-d' || '--pgo' }} -p ${{ matrix.architecture }}
./PCbuild/rt.bat ${{ matrix.debug && '-d' }} -p ${{ matrix.architecture }} -q --exclude ${{ matrix.exclude }} --multiprocess 0 --timeout 3600 --verbose2 --verbose3
./PCbuild/rt.bat ${{ matrix.debug && '-d' || '' }} -p ${{ matrix.architecture }} -q --multiprocess 0 --timeout 4500 --verbose2 --verbose3
# No PGO or tests (yet):
- name: Emulated Windows
if: runner.os == 'Windows' && matrix.architecture == 'ARM64'
run: |
choco upgrade llvm -y
choco install llvm --allow-downgrade --no-progress --version ${{ matrix.llvm }}
./PCbuild/build.bat --experimental-jit ${{ matrix.debug && '-d' || '' }} -p ${{ matrix.architecture }}
- name: Native macOS
if: runner.os == 'macOS'
run: |
brew update
brew install llvm@${{ matrix.llvm }}
SDKROOT="$(xcrun --show-sdk-path)" \
./configure --enable-experimental-jit ${{ matrix.debug && '--with-pydebug' || '--enable-optimizations --with-lto' }}
make all --jobs 4
./python.exe -m test --exclude ${{ matrix.exclude }} --multiprocess 0 --timeout 3600 --verbose2 --verbose3
./python.exe -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3
# --with-lto has been removed temporarily as a result of an open issue in LLVM 18 (see https://github.com/llvm/llvm-project/issues/87553)
- name: Native Linux
if: runner.os == 'Linux' && matrix.architecture == 'x86_64'
run: |
sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" ./llvm.sh ${{ matrix.llvm }}
export PATH="$(llvm-config-${{ matrix.llvm }} --bindir):$PATH"
./configure --enable-experimental-jit ${{ matrix.debug && '--with-pydebug' || '--enable-optimizations --with-lto' }}
./configure --enable-experimental-jit ${{ matrix.debug && '--with-pydebug' || '--enable-optimizations' }}
make all --jobs 4
./python -m test --exclude ${{ matrix.exclude }} --multiprocess 0 --timeout 3600 --verbose2 --verbose3
./python -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3
# --with-lto has been removed temporarily as a result of an open issue in LLVM 18 (see https://github.com/llvm/llvm-project/issues/87553)
- name: Emulated Linux
if: runner.os == 'Linux' && matrix.architecture != 'x86_64'
# The --ignorefile on ./python -m test is used to exclude tests known to fail when running on an emulated Linux.
run: |
sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" ./llvm.sh ${{ matrix.llvm }}
export PATH="$(llvm-config-${{ matrix.llvm }} --bindir):$PATH"
@ -138,6 +141,6 @@ jobs:
CC="${{ matrix.compiler == 'clang' && 'clang --target=$HOST' || '$HOST-gcc' }}" \
CPP="$CC --preprocess" \
HOSTRUNNER=qemu-${{ matrix.architecture }} \
./configure --enable-experimental-jit ${{ matrix.debug && '--with-pydebug' || '--enable-optimizations --with-lto' }} --build=x86_64-linux-gnu --host="$HOST" --with-build-python=../build/bin/python3 --with-pkg-config=no ac_cv_buggy_getaddrinfo=no ac_cv_file__dev_ptc=no ac_cv_file__dev_ptmx=yes
./configure --enable-experimental-jit ${{ matrix.debug && '--with-pydebug' || '--enable-optimizations ' }} --build=x86_64-linux-gnu --host="$HOST" --with-build-python=../build/bin/python3 --with-pkg-config=no ac_cv_buggy_getaddrinfo=no ac_cv_file__dev_ptc=no ac_cv_file__dev_ptmx=yes
make all --jobs 4
./python -m test --exclude ${{ matrix.exclude }} --multiprocess 0 --timeout 3600 --verbose2 --verbose3
./python -m test --ignorefile=Tools/jit/ignore-tests-emulated-linux.txt --multiprocess 0 --timeout 4500 --verbose2 --verbose3

View file

@ -8,6 +8,7 @@ on:
pull_request:
paths:
- ".github/workflows/mypy.yml"
- "Lib/_pyrepl/**"
- "Lib/test/libregrtest/**"
- "Tools/build/generate_sbom.py"
- "Tools/cases_generator/**"
@ -35,8 +36,9 @@ jobs:
strategy:
matrix:
target: [
"Lib/_pyrepl",
"Lib/test/libregrtest",
"Tools/build/",
"Tools/build",
"Tools/cases_generator",
"Tools/clinic",
"Tools/jit",

View file

@ -15,6 +15,7 @@ apt-get -yq install \
libgdbm-dev \
libgdbm-compat-dev \
liblzma-dev \
libmpdec-dev \
libncurses5-dev \
libreadline6-dev \
libsqlite3-dev \

View file

@ -23,7 +23,7 @@ jobs:
- { project: 32, label: sprint }
steps:
- uses: actions/add-to-project@v0.6.0
- uses: actions/add-to-project@v1.0.0
with:
project-url: https://github.com/orgs/python/projects/${{ matrix.project }}
github-token: ${{ secrets.ADD_TO_PROJECT_PAT }}

View file

@ -74,7 +74,7 @@ jobs:
- name: 'Set up Python'
uses: actions/setup-python@v5
with:
python-version: '3.11' # known to work with Sphinx 4.2
python-version: '3.12' # known to work with Sphinx 6.2.1
cache: 'pip'
cache-dependency-path: 'Doc/requirements-oldest-sphinx.txt'
- name: 'Install build dependencies'

View file

@ -22,6 +22,7 @@ jobs:
HOMEBREW_NO_INSTALL_CLEANUP: 1
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: 1
PYTHONSTRICTEXTENSIONBUILD: 1
TERM: linux
strategy:
fail-fast: false
matrix:
@ -49,7 +50,7 @@ jobs:
--prefix=/opt/python-dev \
--with-openssl="$(brew --prefix openssl@3.0)"
- name: Build CPython
run: make -j4
run: make -j8
- name: Display build info
run: make pythoninfo
- name: Tests

View file

@ -7,6 +7,14 @@ on:
options:
required: true
type: string
suppressions_path:
description: 'A repo relative path to the suppressions file'
required: true
type: string
tsan_logs_artifact_name:
description: 'Name of the TSAN logs artifact. Must be unique for each job.'
required: true
type: string
jobs:
build_tsan_reusable:
@ -25,12 +33,19 @@ jobs:
- name: Install Dependencies
run: |
sudo ./.github/workflows/posix-deps-apt.sh
sudo apt install -y clang
# Install clang-18
wget https://apt.llvm.org/llvm.sh
chmod +x llvm.sh
sudo ./llvm.sh 18
sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-18 100
sudo update-alternatives --set clang /usr/bin/clang-18
sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-18 100
sudo update-alternatives --set clang++ /usr/bin/clang++-18
# Reduce ASLR to avoid TSAN crashing
sudo sysctl -w vm.mmap_rnd_bits=28
- name: TSAN Option Setup
run: |
echo "TSAN_OPTIONS=suppressions=${GITHUB_WORKSPACE}/Tools/tsan/supressions.txt" >> $GITHUB_ENV
echo "TSAN_OPTIONS=log_path=${GITHUB_WORKSPACE}/tsan_log suppressions=${GITHUB_WORKSPACE}/${{ inputs.suppressions_path }} handle_segv=0" >> $GITHUB_ENV
echo "CC=clang" >> $GITHUB_ENV
echo "CXX=clang++" >> $GITHUB_ENV
- name: Add ccache to PATH
@ -49,3 +64,13 @@ jobs:
run: make pythoninfo
- name: Tests
run: ./python -m test --tsan -j4
- name: Display TSAN logs
if: always()
run: find ${GITHUB_WORKSPACE} -name 'tsan_log.*' | xargs head -n 1000
- name: Archive TSAN logs
if: always()
uses: actions/upload-artifact@v4
with:
name: ${{ inputs.tsan_logs_artifact_name }}
path: tsan_log.*
if-no-files-found: ignore

View file

@ -12,10 +12,12 @@ jobs:
build_ubuntu_reusable:
name: 'build and test'
timeout-minutes: 60
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
env:
FORCE_COLOR: 1
OPENSSL_VER: 3.0.13
PYTHONSTRICTEXTENSIONBUILD: 1
TERM: linux
steps:
- uses: actions/checkout@v4
- name: Register gcc problem matcher

View file

@ -9,7 +9,7 @@ jobs:
build_wasi_reusable:
name: 'build and test'
timeout-minutes: 60
runs-on: ubuntu-20.04
runs-on: ubuntu-22.04
env:
WASMTIME_VERSION: 18.0.3
WASI_SDK_VERSION: 21
@ -50,7 +50,8 @@ jobs:
uses: actions/cache@v4
with:
path: ${{ env.CROSS_BUILD_PYTHON }}/config.cache
key: ${{ github.job }}-${{ runner.os }}-${{ env.IMAGE_VERSION }}-${{ inputs.config_hash }}
# Include env.pythonLocation in key to avoid changes in environment when setup-python updates Python
key: ${{ github.job }}-${{ runner.os }}-${{ env.IMAGE_VERSION }}-${{ inputs.config_hash }}-${{ env.pythonLocation }}
- name: "Configure build Python"
run: python3 Tools/wasm/wasi.py configure-build-python -- --config-cache --with-pydebug
- name: "Make build Python"
@ -59,7 +60,8 @@ jobs:
uses: actions/cache@v4
with:
path: ${{ env.CROSS_BUILD_WASI }}/config.cache
key: ${{ github.job }}-${{ runner.os }}-${{ env.IMAGE_VERSION }}-wasi-sdk-${{ env.WASI_SDK_VERSION }}-${{ inputs.config_hash }}
# Include env.pythonLocation in key to avoid changes in environment when setup-python updates Python
key: ${{ github.job }}-${{ runner.os }}-${{ env.IMAGE_VERSION }}-wasi-sdk-${{ env.WASI_SDK_VERSION }}-${{ inputs.config_hash }}-${{ env.pythonLocation }}
- name: "Configure host"
# `--with-pydebug` inferred from configure-build-python
run: python3 Tools/wasm/wasi.py configure-host -- --config-cache

View file

@ -1,6 +1,6 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.2.0
rev: v0.3.4
hooks:
- id: ruff
name: Run Ruff on Lib/test/
@ -11,9 +11,19 @@ repos:
args: [--exit-non-zero-on-fix, --config=Tools/clinic/.ruff.toml]
files: ^Tools/clinic/|Lib/test/test_clinic.py
- repo: https://github.com/psf/black-pre-commit-mirror
rev: 24.4.2
hooks:
- id: black
name: Run Black on Tools/jit/
files: ^Tools/jit/
language_version: python3.12
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
- id: check-case-conflict
- id: check-merge-conflict
- id: check-toml
exclude: ^Lib/test/test_tomllib/
- id: check-yaml

View file

@ -22,12 +22,25 @@ you don't already have the SDK, here's how to install it:
`android-sdk/cmdline-tools/latest`.
* `export ANDROID_HOME=/path/to/android-sdk`
The `android.py` script also requires the following commands to be on the `PATH`:
* `curl`
* `java`
* `tar`
* `unzip`
## Building
Building for Android requires doing a cross-build where you have a "build"
Python to help produce an Android build of CPython. This procedure has been
tested on Linux and macOS.
Python can be built for Android on any POSIX platform supported by the Android
development tools, which currently means Linux or macOS. This involves doing a
cross-build where you use a "build" Python (for your development machine) to
help produce a "host" Python for Android.
First, make sure you have all the usual tools and libraries needed to build
Python for your development machine. The only Android tool you need to install
is the command line tools package above: the build script will download the
rest.
The easiest way to do a build is to use the `android.py` script. You can either
have it perform the entire build process from start to finish in one step, or
@ -43,9 +56,10 @@ The discrete steps for building via `android.py` are:
./android.py make-host HOST
```
To see the possible values of HOST, run `./android.py configure-host --help`.
`HOST` identifies which architecture to build. To see the possible values, run
`./android.py configure-host --help`.
Or to do it all in a single command, run:
To do all steps in a single command, run:
```sh
./android.py build HOST
@ -62,3 +76,22 @@ call. For example, if you want a pydebug build that also caches the results from
```sh
./android.py build HOST -- -C --with-pydebug
```
## Testing
To run the Python test suite on Android:
* Install Android Studio, if you don't already have it.
* Follow the instructions in the previous section to build all supported
architectures.
* Run `./android.py setup-testbed` to download the Gradle wrapper.
* Open the `testbed` directory in Android Studio.
* In the *Device Manager* dock, connect a device or start an emulator.
Then select it from the drop-down list in the toolbar.
* Click the "Run" button in the toolbar.
* The testbed app displays nothing on screen while running. To see its output,
open the [Logcat window](https://developer.android.com/studio/debug/logcat).
To run specific tests, or pass any other arguments to the test suite, edit the
command line in testbed/app/src/main/python/main.py.

View file

@ -61,6 +61,12 @@ done
export CFLAGS=""
export LDFLAGS="-Wl,--build-id=sha1 -Wl,--no-rosegment"
# Unlike Linux, Android does not implicitly use a dlopened library to resolve
# relocations in subsequently-loaded libraries, even if RTLD_GLOBAL is used
# (https://github.com/android/ndk/issues/1244). So any library that fails to
# build with this flag, would also fail to load at runtime.
LDFLAGS="$LDFLAGS -Wl,--no-undefined"
# Many packages get away with omitting -lm on Linux, but Android is stricter.
LDFLAGS="$LDFLAGS -lm"

View file

@ -7,8 +7,9 @@ import shutil
import subprocess
import sys
import sysconfig
from os.path import relpath
from os.path import basename, relpath
from pathlib import Path
from tempfile import TemporaryDirectory
SCRIPT_NAME = Path(__file__).name
CHECKOUT = Path(__file__).resolve().parent.parent
@ -102,11 +103,17 @@ def unpack_deps(host):
for name_ver in ["bzip2-1.0.8-1", "libffi-3.4.4-2", "openssl-3.0.13-1",
"sqlite-3.45.1-0", "xz-5.4.6-0"]:
filename = f"{name_ver}-{host}.tar.gz"
run(["wget", f"{deps_url}/{name_ver}/{filename}"])
download(f"{deps_url}/{name_ver}/{filename}")
run(["tar", "-xf", filename])
os.remove(filename)
def download(url, target_dir="."):
out_path = f"{target_dir}/{basename(url)}"
run(["curl", "-Lf", "-o", out_path, url])
return out_path
def configure_host_python(context):
host_dir = subdir(context.host, clean=context.clean)
@ -160,6 +167,30 @@ def clean_all(context):
delete_if_exists(CROSS_BUILD_DIR)
# To avoid distributing compiled artifacts without corresponding source code,
# the Gradle wrapper is not included in the CPython repository. Instead, we
# extract it from the Gradle release.
def setup_testbed(context):
ver_long = "8.7.0"
ver_short = ver_long.removesuffix(".0")
testbed_dir = CHECKOUT / "Android/testbed"
for filename in ["gradlew", "gradlew.bat"]:
out_path = download(
f"https://raw.githubusercontent.com/gradle/gradle/v{ver_long}/{filename}",
testbed_dir)
os.chmod(out_path, 0o755)
with TemporaryDirectory(prefix=SCRIPT_NAME) as temp_dir:
os.chdir(temp_dir)
bin_zip = download(
f"https://services.gradle.org/distributions/gradle-{ver_short}-bin.zip")
outer_jar = f"gradle-{ver_short}/lib/plugins/gradle-wrapper-{ver_short}.jar"
run(["unzip", bin_zip, outer_jar])
run(["unzip", "-o", "-d", f"{testbed_dir}/gradle/wrapper", outer_jar,
"gradle-wrapper.jar"])
def main():
parser = argparse.ArgumentParser()
subcommands = parser.add_subparsers(dest="subcommand")
@ -173,8 +204,11 @@ def main():
help="Run `configure` for Android")
make_host = subcommands.add_parser("make-host",
help="Run `make` for Android")
clean = subcommands.add_parser("clean", help="Delete files and directories "
"created by this script")
subcommands.add_parser(
"clean", help="Delete the cross-build directory")
subcommands.add_parser(
"setup-testbed", help="Download the testbed Gradle wrapper")
for subcommand in build, configure_build, configure_host:
subcommand.add_argument(
"--clean", action="store_true", default=False, dest="clean",
@ -194,7 +228,8 @@ def main():
"configure-host": configure_host_python,
"make-host": make_host_python,
"build": build_all,
"clean": clean_all}
"clean": clean_all,
"setup-testbed": setup_testbed}
dispatch[context.subcommand](context)

21
Android/testbed/.gitignore vendored Normal file
View file

@ -0,0 +1,21 @@
# The Gradle wrapper should be downloaded by running `../android.py setup-testbed`.
/gradlew
/gradlew.bat
/gradle/wrapper/gradle-wrapper.jar
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/deploymentTargetDropdown.xml
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties

1
Android/testbed/app/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/build

View file

@ -0,0 +1,129 @@
import com.android.build.api.variant.*
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
}
val PYTHON_DIR = File(projectDir, "../../..").canonicalPath
val PYTHON_CROSS_DIR = "$PYTHON_DIR/cross-build"
val ABIS = mapOf(
"arm64-v8a" to "aarch64-linux-android",
"x86_64" to "x86_64-linux-android",
)
val PYTHON_VERSION = File("$PYTHON_DIR/Include/patchlevel.h").useLines {
for (line in it) {
val match = """#define PY_VERSION\s+"(\d+\.\d+)""".toRegex().find(line)
if (match != null) {
return@useLines match.groupValues[1]
}
}
throw GradleException("Failed to find Python version")
}
android {
namespace = "org.python.testbed"
compileSdk = 34
defaultConfig {
applicationId = "org.python.testbed"
minSdk = 21
targetSdk = 34
versionCode = 1
versionName = "1.0"
ndk.abiFilters.addAll(ABIS.keys)
externalNativeBuild.cmake.arguments(
"-DPYTHON_CROSS_DIR=$PYTHON_CROSS_DIR",
"-DPYTHON_VERSION=$PYTHON_VERSION")
}
externalNativeBuild.cmake {
path("src/main/c/CMakeLists.txt")
}
// Set this property to something non-empty, otherwise it'll use the default
// list, which ignores asset directories beginning with an underscore.
aaptOptions.ignoreAssetsPattern = ".git"
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
}
dependencies {
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.11.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
}
// Create some custom tasks to copy Python and its standard library from
// elsewhere in the repository.
androidComponents.onVariants { variant ->
generateTask(variant, variant.sources.assets!!) {
into("python") {
for (triplet in ABIS.values) {
for (subDir in listOf("include", "lib")) {
into(subDir) {
from("$PYTHON_CROSS_DIR/$triplet/prefix/$subDir")
include("python$PYTHON_VERSION/**")
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}
}
}
into("lib/python$PYTHON_VERSION") {
// Uncomment this to pick up edits from the source directory
// without having to rerun `make install`.
// from("$PYTHON_DIR/Lib")
// duplicatesStrategy = DuplicatesStrategy.INCLUDE
into("site-packages") {
from("$projectDir/src/main/python")
}
}
}
exclude("**/__pycache__")
}
generateTask(variant, variant.sources.jniLibs!!) {
for ((abi, triplet) in ABIS.entries) {
into(abi) {
from("$PYTHON_CROSS_DIR/$triplet/prefix/lib")
include("libpython*.*.so")
include("lib*_python.so")
}
}
}
}
fun generateTask(
variant: ApplicationVariant, directories: SourceDirectories,
configure: GenerateTask.() -> Unit
) {
val taskName = "generate" +
listOf(variant.name, "Python", directories.name)
.map { it.replaceFirstChar(Char::uppercase) }
.joinToString("")
directories.addGeneratedSourceDirectory(
tasks.register<GenerateTask>(taskName) {
into(outputDir)
configure()
},
GenerateTask::outputDir)
}
// addGeneratedSourceDirectory requires the task to have a DirectoryProperty.
abstract class GenerateTask: Sync() {
@get:OutputDirectory
abstract val outputDir: DirectoryProperty
}

View file

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET"/>
<application
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/Theme.Material3.Light.NoActionBar">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View file

@ -0,0 +1,9 @@
cmake_minimum_required(VERSION 3.4.1)
project(testbed)
set(PREFIX_DIR ${PYTHON_CROSS_DIR}/${CMAKE_LIBRARY_ARCHITECTURE}/prefix)
include_directories(${PREFIX_DIR}/include/python${PYTHON_VERSION})
link_directories(${PREFIX_DIR}/lib)
link_libraries(log python${PYTHON_VERSION})
add_library(main_activity SHARED main_activity.c)

View file

@ -0,0 +1,147 @@
#include <android/log.h>
#include <errno.h>
#include <jni.h>
#include <pthread.h>
#include <Python.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
static void throw_runtime_exception(JNIEnv *env, const char *message) {
(*env)->ThrowNew(
env,
(*env)->FindClass(env, "java/lang/RuntimeException"),
message);
}
// --- Stdio redirection ------------------------------------------------------
// Most apps won't need this, because the Python-level sys.stdout and sys.stderr
// are redirected to the Android logcat by Python itself. However, in the
// testbed it's useful to redirect the native streams as well, to debug problems
// in the Python startup or redirection process.
//
// Based on
// https://github.com/beeware/briefcase-android-gradle-template/blob/v0.3.11/%7B%7B%20cookiecutter.safe_formal_name%20%7D%7D/app/src/main/cpp/native-lib.cpp
typedef struct {
FILE *file;
int fd;
android_LogPriority priority;
char *tag;
int pipe[2];
} StreamInfo;
static StreamInfo STREAMS[] = {
{stdout, STDOUT_FILENO, ANDROID_LOG_INFO, "native.stdout", {-1, -1}},
{stderr, STDERR_FILENO, ANDROID_LOG_WARN, "native.stderr", {-1, -1}},
{NULL, -1, ANDROID_LOG_UNKNOWN, NULL, {-1, -1}},
};
// The maximum length of a log message in bytes, including the level marker and
// tag, is defined as LOGGER_ENTRY_MAX_PAYLOAD in
// platform/system/logging/liblog/include/log/log.h. As of API level 30, messages
// longer than this will be be truncated by logcat. This limit has already been
// reduced at least once in the history of Android (from 4076 to 4068 between API
// level 23 and 26), so leave some headroom.
static const int MAX_BYTES_PER_WRITE = 4000;
static void *redirection_thread(void *arg) {
StreamInfo *si = (StreamInfo*)arg;
ssize_t read_size;
char buf[MAX_BYTES_PER_WRITE];
while ((read_size = read(si->pipe[0], buf, sizeof buf - 1)) > 0) {
buf[read_size] = '\0'; /* add null-terminator */
__android_log_write(si->priority, si->tag, buf);
}
return 0;
}
static char *redirect_stream(StreamInfo *si) {
/* make the FILE unbuffered, to ensure messages are never lost */
if (setvbuf(si->file, 0, _IONBF, 0)) {
return "setvbuf";
}
/* create the pipe and redirect the file descriptor */
if (pipe(si->pipe)) {
return "pipe";
}
if (dup2(si->pipe[1], si->fd) == -1) {
return "dup2";
}
/* start the logging thread */
pthread_t thr;
if ((errno = pthread_create(&thr, 0, redirection_thread, si))) {
return "pthread_create";
}
if ((errno = pthread_detach(thr))) {
return "pthread_detach";
}
return 0;
}
JNIEXPORT void JNICALL Java_org_python_testbed_MainActivity_redirectStdioToLogcat(
JNIEnv *env, jobject obj
) {
for (StreamInfo *si = STREAMS; si->file; si++) {
char *error_prefix;
if ((error_prefix = redirect_stream(si))) {
char error_message[1024];
snprintf(error_message, sizeof(error_message),
"%s: %s", error_prefix, strerror(errno));
throw_runtime_exception(env, error_message);
return;
}
}
}
// --- Python intialization ----------------------------------------------------
static PyStatus set_config_string(
JNIEnv *env, PyConfig *config, wchar_t **config_str, jstring value
) {
const char *value_utf8 = (*env)->GetStringUTFChars(env, value, NULL);
PyStatus status = PyConfig_SetBytesString(config, config_str, value_utf8);
(*env)->ReleaseStringUTFChars(env, value, value_utf8);
return status;
}
static void throw_status(JNIEnv *env, PyStatus status) {
throw_runtime_exception(env, status.err_msg ? status.err_msg : "");
}
JNIEXPORT void JNICALL Java_org_python_testbed_MainActivity_runPython(
JNIEnv *env, jobject obj, jstring home, jstring runModule
) {
PyConfig config;
PyStatus status;
PyConfig_InitIsolatedConfig(&config);
status = set_config_string(env, &config, &config.home, home);
if (PyStatus_Exception(status)) {
throw_status(env, status);
return;
}
status = set_config_string(env, &config, &config.run_module, runModule);
if (PyStatus_Exception(status)) {
throw_status(env, status);
return;
}
// Some tests generate SIGPIPE and SIGXFSZ, which should be ignored.
config.install_signal_handlers = 1;
status = Py_InitializeFromConfig(&config);
if (PyStatus_Exception(status)) {
throw_status(env, status);
return;
}
Py_RunMain();
}

View file

@ -0,0 +1,61 @@
package org.python.testbed
import android.os.*
import android.system.Os
import android.widget.TextView
import androidx.appcompat.app.*
import java.io.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Python needs this variable to help it find the temporary directory,
// but Android only sets it on API level 33 and later.
Os.setenv("TMPDIR", cacheDir.toString(), false)
val pythonHome = extractAssets()
System.loadLibrary("main_activity")
redirectStdioToLogcat()
runPython(pythonHome.toString(), "main")
findViewById<TextView>(R.id.tvHello).text = "Python complete"
}
private fun extractAssets() : File {
val pythonHome = File(filesDir, "python")
if (pythonHome.exists() && !pythonHome.deleteRecursively()) {
throw RuntimeException("Failed to delete $pythonHome")
}
extractAssetDir("python", filesDir)
return pythonHome
}
private fun extractAssetDir(path: String, targetDir: File) {
val names = assets.list(path)
?: throw RuntimeException("Failed to list $path")
val targetSubdir = File(targetDir, path)
if (!targetSubdir.mkdirs()) {
throw RuntimeException("Failed to create $targetSubdir")
}
for (name in names) {
val subPath = "$path/$name"
val input: InputStream
try {
input = assets.open(subPath)
} catch (e: FileNotFoundException) {
extractAssetDir(subPath, targetDir)
continue
}
input.use {
File(targetSubdir, name).outputStream().use { output ->
input.copyTo(output)
}
}
}
}
private external fun redirectStdioToLogcat()
private external fun runPython(home: String, runModule: String)
}

View file

@ -0,0 +1,17 @@
import runpy
import signal
import sys
# Some tests use SIGUSR1, but that's blocked by default in an Android app in
# order to make it available to `sigwait` in the "Signal Catcher" thread. That
# thread's functionality is only relevant to the JVM ("forcing GC (no HPROF) and
# profile save"), so disabling it should not weaken the tests.
signal.pthread_sigmask(signal.SIG_UNBLOCK, [signal.SIGUSR1])
# To run specific tests, or pass any other arguments to the test suite, edit
# this command line.
sys.argv[1:] = [
"--use", "all,-cpu",
"--verbose3",
]
runpy.run_module("test")

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/tvHello"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,3 @@
<resources>
<string name="app_name">Python testbed</string>
</resources>

View file

@ -0,0 +1,5 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
id("com.android.application") version "8.2.2" apply false
id("org.jetbrains.kotlin.android") version "1.9.22" apply false
}

View file

@ -0,0 +1,23 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app's APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
# Enables namespacing of each library's R class so that its R class includes only the
# resources declared in the library itself and none from the library's dependencies,
# thereby reducing the size of the R class for that library
android.nonTransitiveRClass=true

View file

@ -0,0 +1,6 @@
#Mon Feb 19 20:29:06 GMT 2024
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View file

@ -0,0 +1,18 @@
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
rootProject.name = "Python testbed"
include(":app")

View file

@ -22,6 +22,10 @@ have a suggestion on how to fix it, include that as well.
You can also open a discussion item on our
`Documentation Discourse forum <https://discuss.python.org/c/documentation/26>`_.
If you find a bug in the theme (HTML / CSS / JavaScript) of the
documentation, please submit a bug report on the `python-doc-theme bug
tracker <https://github.com/python/python-docs-theme>`_.
If you're short on time, you can also email documentation bug reports to
docs@python.org (behavioral bugs can be sent to python-list@python.org).
'docs@' is a mailing list run by volunteers; your request will be noticed,

View file

@ -191,10 +191,10 @@ called with a non-bytes parameter.
.. c:function:: int _PyBytes_Resize(PyObject **bytes, Py_ssize_t newsize)
A way to resize a bytes object even though it is "immutable". Only use this
to build up a brand new bytes object; don't use this if the bytes may already
be known in other parts of the code. It is an error to call this function if
the refcount on the input bytes object is not one. Pass the address of an
Resize a bytes object. *newsize* will be the new length of the bytes object.
You can think of it as creating a new bytes object and destroying the old
one, only more efficiently.
Pass the address of an
existing bytes object as an lvalue (it may be written into), and the new size
desired. On success, *\*bytes* holds the resized bytes object and ``0`` is
returned; the address in *\*bytes* may differ from its input value. If the

View file

@ -104,7 +104,7 @@ Printing and clearing
Similar to :c:func:`PyErr_WriteUnraisable`, but the *format* and subsequent
parameters help format the warning message; they have the same meaning and
values as in :c:func:`PyUnicode_FromFormat`.
``PyErr_WriteUnraisable(obj)`` is roughtly equivalent to
``PyErr_WriteUnraisable(obj)`` is roughly equivalent to
``PyErr_FormatUnraisable("Exception ignored in: %R", obj)``.
If *format* is ``NULL``, only the traceback is printed.
@ -221,13 +221,14 @@ For convenience, some of these functions will always return a
.. c:function:: PyObject* PyErr_SetFromWindowsErr(int ierr)
This is a convenience function to raise :exc:`WindowsError`. If called with
This is a convenience function to raise :exc:`OSError`. If called with
*ierr* of ``0``, the error code returned by a call to :c:func:`!GetLastError`
is used instead. It calls the Win32 function :c:func:`!FormatMessage` to retrieve
the Windows description of error code given by *ierr* or :c:func:`!GetLastError`,
then it constructs a tuple object whose first item is the *ierr* value and whose
second item is the corresponding error message (gotten from
:c:func:`!FormatMessage`), and then calls ``PyErr_SetObject(PyExc_WindowsError,
then it constructs a :exc:`OSError` object with the :attr:`~OSError.winerror`
attribute set to the error code, the :attr:`~OSError.strerror` attribute
set to the corresponding error message (gotten from
:c:func:`!FormatMessage`), and then calls ``PyErr_SetObject(PyExc_OSError,
object)``. This function always returns ``NULL``.
.. availability:: Windows.

View file

@ -120,12 +120,19 @@ See also :ref:`Reflection <reflection>`.
.. c:function:: PyObject* PyFrame_GetLocals(PyFrameObject *frame)
Get the *frame*'s :attr:`~frame.f_locals` attribute (:class:`dict`).
Get the *frame*'s :attr:`~frame.f_locals` attribute.
If the frame refers to a function or comprehension, this returns
a write-through proxy object that allows modifying the locals.
In all other cases (classes, modules) it returns the :class:`dict`
representing the frame locals directly.
Return a :term:`strong reference`.
.. versionadded:: 3.11
.. versionchanged:: 3.13
Return a proxy object for functions and comprehensions.
.. c:function:: int PyFrame_GetLineNumber(PyFrameObject *frame)

View file

@ -82,3 +82,14 @@ See also the :c:member:`PyTypeObject.tp_hash` member and :ref:`numeric-hash`.
The function cannot fail: it cannot return ``-1``.
.. versionadded:: 3.13
.. c:function:: Py_hash_t PyObject_GenericHash(PyObject *obj)
Generic hashing function that is meant to be put into a type
object's ``tp_hash`` slot.
Its result only depends on the object's identity.
.. impl-detail::
In CPython, it is equivalent to :c:func:`Py_HashPointer`.
.. versionadded:: 3.13

View file

@ -25,3 +25,4 @@ document the API functions in detail.
memory.rst
objimpl.rst
apiabiversion.rst
monitoring.rst

View file

@ -29,6 +29,8 @@ The following functions can be safely called before Python is initialized:
* :c:func:`PyMem_SetAllocator`
* :c:func:`PyMem_SetupDebugHooks`
* :c:func:`PyObject_SetArenaAllocator`
* :c:func:`Py_SetProgramName`
* :c:func:`Py_SetPythonHome`
* :c:func:`PySys_ResetWarnOptions`
* Informative functions:
@ -59,7 +61,7 @@ The following functions can be safely called before Python is initialized:
:c:func:`Py_Initialize`: :c:func:`Py_EncodeLocale`, :c:func:`Py_GetPath`,
:c:func:`Py_GetPrefix`, :c:func:`Py_GetExecPrefix`,
:c:func:`Py_GetProgramFullPath`, :c:func:`Py_GetPythonHome`,
and :c:func:`Py_GetProgramName`.
:c:func:`Py_GetProgramName` and :c:func:`PyEval_InitThreads`.
.. _global-conf-vars:
@ -326,6 +328,7 @@ Initializing and finalizing the interpreter
.. c:function:: void Py_Initialize()
.. index::
single: PyEval_InitThreads()
single: modules (in module sys)
single: path (in module sys)
pair: module; builtins
@ -425,6 +428,34 @@ Process-wide parameters
=======================
.. c:function:: void Py_SetProgramName(const wchar_t *name)
.. index::
single: Py_Initialize()
single: main()
single: Py_GetPath()
This API is kept for backward compatibility: setting
:c:member:`PyConfig.program_name` should be used instead, see :ref:`Python
Initialization Configuration <init-config>`.
This function should be called before :c:func:`Py_Initialize` is called for
the first time, if it is called at all. It tells the interpreter the value
of the ``argv[0]`` argument to the :c:func:`main` function of the program
(converted to wide characters).
This is used by :c:func:`Py_GetPath` and some other functions below to find
the Python run-time libraries relative to the interpreter executable. The
default value is ``'python'``. The argument should point to a
zero-terminated wide character string in static storage whose contents will not
change for the duration of the program's execution. No code in the Python
interpreter will change the contents of this storage.
Use :c:func:`Py_DecodeLocale` to decode a bytes string to get a
:c:expr:`wchar_*` string.
.. deprecated:: 3.11
.. c:function:: wchar_t* Py_GetProgramName()
Return the program name set with :c:member:`PyConfig.program_name`, or the default.
@ -626,6 +657,106 @@ Process-wide parameters
``sys.version``.
.. c:function:: void PySys_SetArgvEx(int argc, wchar_t **argv, int updatepath)
.. index::
single: main()
single: Py_FatalError()
single: argv (in module sys)
This API is kept for backward compatibility: setting
:c:member:`PyConfig.argv`, :c:member:`PyConfig.parse_argv` and
:c:member:`PyConfig.safe_path` should be used instead, see :ref:`Python
Initialization Configuration <init-config>`.
Set :data:`sys.argv` based on *argc* and *argv*. These parameters are
similar to those passed to the program's :c:func:`main` function with the
difference that the first entry should refer to the script file to be
executed rather than the executable hosting the Python interpreter. If there
isn't a script that will be run, the first entry in *argv* can be an empty
string. If this function fails to initialize :data:`sys.argv`, a fatal
condition is signalled using :c:func:`Py_FatalError`.
If *updatepath* is zero, this is all the function does. If *updatepath*
is non-zero, the function also modifies :data:`sys.path` according to the
following algorithm:
- If the name of an existing script is passed in ``argv[0]``, the absolute
path of the directory where the script is located is prepended to
:data:`sys.path`.
- Otherwise (that is, if *argc* is ``0`` or ``argv[0]`` doesn't point
to an existing file name), an empty string is prepended to
:data:`sys.path`, which is the same as prepending the current working
directory (``"."``).
Use :c:func:`Py_DecodeLocale` to decode a bytes string to get a
:c:expr:`wchar_*` string.
See also :c:member:`PyConfig.orig_argv` and :c:member:`PyConfig.argv`
members of the :ref:`Python Initialization Configuration <init-config>`.
.. note::
It is recommended that applications embedding the Python interpreter
for purposes other than executing a single script pass ``0`` as *updatepath*,
and update :data:`sys.path` themselves if desired.
See :cve:`2008-5983`.
On versions before 3.1.3, you can achieve the same effect by manually
popping the first :data:`sys.path` element after having called
:c:func:`PySys_SetArgv`, for example using::
PyRun_SimpleString("import sys; sys.path.pop(0)\n");
.. versionadded:: 3.1.3
.. XXX impl. doesn't seem consistent in allowing ``0``/``NULL`` for the params;
check w/ Guido.
.. deprecated:: 3.11
.. c:function:: void PySys_SetArgv(int argc, wchar_t **argv)
This API is kept for backward compatibility: setting
:c:member:`PyConfig.argv` and :c:member:`PyConfig.parse_argv` should be used
instead, see :ref:`Python Initialization Configuration <init-config>`.
This function works like :c:func:`PySys_SetArgvEx` with *updatepath* set
to ``1`` unless the :program:`python` interpreter was started with the
:option:`-I`.
Use :c:func:`Py_DecodeLocale` to decode a bytes string to get a
:c:expr:`wchar_*` string.
See also :c:member:`PyConfig.orig_argv` and :c:member:`PyConfig.argv`
members of the :ref:`Python Initialization Configuration <init-config>`.
.. versionchanged:: 3.4 The *updatepath* value depends on :option:`-I`.
.. deprecated:: 3.11
.. c:function:: void Py_SetPythonHome(const wchar_t *home)
This API is kept for backward compatibility: setting
:c:member:`PyConfig.home` should be used instead, see :ref:`Python
Initialization Configuration <init-config>`.
Set the default "home" directory, that is, the location of the standard
Python libraries. See :envvar:`PYTHONHOME` for the meaning of the
argument string.
The argument should point to a zero-terminated character string in static
storage whose contents will not change for the duration of the program's
execution. No code in the Python interpreter will change the contents of
this storage.
Use :c:func:`Py_DecodeLocale` to decode a bytes string to get a
:c:expr:`wchar_*` string.
.. deprecated:: 3.11
.. c:function:: wchar_t* Py_GetPythonHome()
Return the default "home", that is, the value set by
@ -841,6 +972,33 @@ code, or when embedding the Python interpreter:
This thread's interpreter state.
.. c:function:: void PyEval_InitThreads()
.. index::
single: PyEval_AcquireThread()
single: PyEval_ReleaseThread()
single: PyEval_SaveThread()
single: PyEval_RestoreThread()
Deprecated function which does nothing.
In Python 3.6 and older, this function created the GIL if it didn't exist.
.. versionchanged:: 3.9
The function now does nothing.
.. versionchanged:: 3.7
This function is now called by :c:func:`Py_Initialize()`, so you don't
have to call it yourself anymore.
.. versionchanged:: 3.2
This function cannot be called before :c:func:`Py_Initialize()` anymore.
.. deprecated:: 3.9
.. index:: pair: module; _thread
.. c:function:: PyThreadState* PyEval_SaveThread()
Release the global interpreter lock (if it has been created) and reset the
@ -1746,6 +1904,58 @@ Python-level trace functions in previous versions.
.. versionadded:: 3.12
Reference tracing
=================
.. versionadded:: 3.13
.. c:type:: int (*PyRefTracer)(PyObject *, int event, void* data)
The type of the trace function registered using :c:func:`PyRefTracer_SetTracer`.
The first parameter is a Python object that has been just created (when **event**
is set to :c:data:`PyRefTracer_CREATE`) or about to be destroyed (when **event**
is set to :c:data:`PyRefTracer_DESTROY`). The **data** argument is the opaque pointer
that was provided when :c:func:`PyRefTracer_SetTracer` was called.
.. versionadded:: 3.13
.. c:var:: int PyRefTracer_CREATE
The value for the *event* parameter to :c:type:`PyRefTracer` functions when a Python
object has been created.
.. c:var:: int PyRefTracer_DESTROY
The value for the *event* parameter to :c:type:`PyRefTracer` functions when a Python
object has been destroyed.
.. c:function:: int PyRefTracer_SetTracer(PyRefTracer tracer, void *data)
Register a reference tracer function. The function will be called when a new
Python has been created or when an object is going to be destroyed. If
**data** is provided it must be an opaque pointer that will be provided when
the tracer function is called. Return ``0`` on success. Set an exception and
return ``-1`` on error.
Not that tracer functions **must not** create Python objects inside or
otherwise the call will be re-entrant. The tracer also **must not** clear
any existing exception or set an exception. The GIL will be held every time
the tracer function is called.
The GIL must be held when calling this function.
.. versionadded:: 3.13
.. c:function:: PyRefTracer PyRefTracer_GetTracer(void** data)
Get the registered reference tracer function and the value of the opaque data
pointer that was registered when :c:func:`PyRefTracer_SetTracer` was called.
If no tracer was registered this function will return NULL and will set the
**data** pointer to NULL.
The GIL must be held when calling this function.
.. versionadded:: 3.13
.. _advanced-debugging:

View file

@ -1250,8 +1250,11 @@ PyConfig
If non-zero, initialize the perf trampoline. See :ref:`perf_profiling`
for more information.
Set by :option:`-X perf <-X>` command line option and by the
:envvar:`PYTHONPERFSUPPORT` environment variable.
Set by :option:`-X perf <-X>` command-line option and by the
:envvar:`PYTHON_PERF_JIT_SUPPORT` environment variable for perf support
with stack pointers and :option:`-X perf_jit <-X>` command-line option
and by the :envvar:`PYTHON_PERF_JIT_SUPPORT` environment variable for perf
support with DWARF JIT information.
Default: ``-1``.

View file

@ -113,24 +113,28 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate.
retrieved from the resulting value using :c:func:`PyLong_AsVoidPtr`.
.. c:function:: PyObject* PyLong_FromNativeBytes(const void* buffer, size_t n_bytes, int endianness)
.. c:function:: PyObject* PyLong_FromNativeBytes(const void* buffer, size_t n_bytes, int flags)
Create a Python integer from the value contained in the first *n_bytes* of
*buffer*, interpreted as a two's-complement signed number.
*endianness* may be passed ``-1`` for the native endian that CPython was
compiled with, or else ``0`` for big endian and ``1`` for little.
*flags* are as for :c:func:`PyLong_AsNativeBytes`. Passing ``-1`` will select
the native endian that CPython was compiled with and assume that the
most-significant bit is a sign bit. Passing
``Py_ASNATIVEBYTES_UNSIGNED_BUFFER`` will produce the same result as calling
:c:func:`PyLong_FromUnsignedNativeBytes`. Other flags are ignored.
.. versionadded:: 3.13
.. c:function:: PyObject* PyLong_FromUnsignedNativeBytes(const void* buffer, size_t n_bytes, int endianness)
.. c:function:: PyObject* PyLong_FromUnsignedNativeBytes(const void* buffer, size_t n_bytes, int flags)
Create a Python integer from the value contained in the first *n_bytes* of
*buffer*, interpreted as an unsigned number.
*endianness* may be passed ``-1`` for the native endian that CPython was
compiled with, or else ``0`` for big endian and ``1`` for little.
*flags* are as for :c:func:`PyLong_AsNativeBytes`. Passing ``-1`` will select
the native endian that CPython was compiled with and assume that the
most-significant bit is not a sign bit. Flags other than endian are ignored.
.. versionadded:: 3.13
@ -354,14 +358,41 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate.
Returns ``NULL`` on error. Use :c:func:`PyErr_Occurred` to disambiguate.
.. c:function:: Py_ssize_t PyLong_AsNativeBytes(PyObject *pylong, void* buffer, Py_ssize_t n_bytes, int endianness)
.. c:function:: Py_ssize_t PyLong_AsNativeBytes(PyObject *pylong, void* buffer, Py_ssize_t n_bytes, int flags)
Copy the Python integer value to a native *buffer* of size *n_bytes*::
Copy the Python integer value *pylong* to a native *buffer* of size
*n_bytes*. The *flags* can be set to ``-1`` to behave similarly to a C cast,
or to values documented below to control the behavior.
Returns ``-1`` with an exception raised on error. This may happen if
*pylong* cannot be interpreted as an integer, or if *pylong* was negative
and the ``Py_ASNATIVEBYTES_REJECT_NEGATIVE`` flag was set.
Otherwise, returns the number of bytes required to store the value.
If this is equal to or less than *n_bytes*, the entire value was copied.
All *n_bytes* of the buffer are written: large buffers are padded with
zeroes.
If the returned value is greater than than *n_bytes*, the value was
truncated: as many of the lowest bits of the value as could fit are written,
and the higher bits are ignored. This matches the typical behavior
of a C-style downcast.
.. note::
Overflow is not considered an error. If the returned value
is larger than *n_bytes*, most significant bits were discarded.
``0`` will never be returned.
Values are always copied as two's-complement.
Usage example::
int32_t value;
Py_ssize_t bytes = PyLong_AsNativeBits(pylong, &value, sizeof(value), -1);
Py_ssize_t bytes = PyLong_AsNativeBytes(pylong, &value, sizeof(value), -1);
if (bytes < 0) {
// A Python exception was set with the reason.
// Failed. A Python exception was set with the reason.
return NULL;
}
else if (bytes <= (Py_ssize_t)sizeof(value)) {
@ -372,19 +403,24 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate.
// lowest bits of pylong.
}
The above example may look *similar* to
:c:func:`PyLong_As* <PyLong_AsSize_t>`
but instead fills in a specific caller defined type and never raises an
error about of the :class:`int` *pylong*'s value regardless of *n_bytes*
or the returned byte count.
Passing zero to *n_bytes* will return the size of a buffer that would
be large enough to hold the value. This may be larger than technically
necessary, but not unreasonably so.
To get at the entire potentially big Python value, this can be used to
reserve enough space and copy it::
.. note::
Passing *n_bytes=0* to this function is not an accurate way to determine
the bit length of a value.
If *n_bytes=0*, *buffer* may be ``NULL``.
To get at the entire Python value of an unknown size, the function can be
called twice: first to determine the buffer size, then to fill it::
// Ask how much space we need.
Py_ssize_t expected = PyLong_AsNativeBits(pylong, NULL, 0, -1);
Py_ssize_t expected = PyLong_AsNativeBytes(pylong, NULL, 0, -1);
if (expected < 0) {
// A Python exception was set with the reason.
// Failed. A Python exception was set with the reason.
return NULL;
}
assert(expected != 0); // Impossible per the API definition.
@ -394,12 +430,12 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate.
return NULL;
}
// Safely get the entire value.
Py_ssize_t bytes = PyLong_AsNativeBits(pylong, bignum, expected, -1);
if (bytes < 0) { // Exception set.
Py_ssize_t bytes = PyLong_AsNativeBytes(pylong, bignum, expected, -1);
if (bytes < 0) { // Exception has been set.
free(bignum);
return NULL;
}
else if (bytes > expected) { // Be safe, should not be possible.
else if (bytes > expected) { // This should not be possible.
PyErr_SetString(PyExc_RuntimeError,
"Unexpected bignum truncation after a size check.");
free(bignum);
@ -409,35 +445,51 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate.
// ... use bignum ...
free(bignum);
*endianness* may be passed ``-1`` for the native endian that CPython was
compiled with, or ``0`` for big endian and ``1`` for little.
*flags* is either ``-1`` (``Py_ASNATIVEBYTES_DEFAULTS``) to select defaults
that behave most like a C cast, or a combintation of the other flags in
the table below.
Note that ``-1`` cannot be combined with other flags.
Returns ``-1`` with an exception raised if *pylong* cannot be interpreted as
an integer. Otherwise, return the size of the buffer required to store the
value. If this is equal to or less than *n_bytes*, the entire value was
copied. ``0`` will never be returned.
Currently, ``-1`` corresponds to
``Py_ASNATIVEBYTES_NATIVE_ENDIAN | Py_ASNATIVEBYTES_UNSIGNED_BUFFER``.
Unless an exception is raised, all *n_bytes* of the buffer will always be
written. In the case of truncation, as many of the lowest bits of the value
as could fit are written. This allows the caller to ignore all non-negative
results if the intent is to match the typical behavior of a C-style
downcast. No exception is set on truncation.
============================================= ======
Flag Value
============================================= ======
.. c:macro:: Py_ASNATIVEBYTES_DEFAULTS ``-1``
.. c:macro:: Py_ASNATIVEBYTES_BIG_ENDIAN ``0``
.. c:macro:: Py_ASNATIVEBYTES_LITTLE_ENDIAN ``1``
.. c:macro:: Py_ASNATIVEBYTES_NATIVE_ENDIAN ``3``
.. c:macro:: Py_ASNATIVEBYTES_UNSIGNED_BUFFER ``4``
.. c:macro:: Py_ASNATIVEBYTES_REJECT_NEGATIVE ``8``
============================================= ======
Values are always copied as two's-complement and sufficient buffer will be
requested to include a sign bit. For example, this may cause an value that
fits into 8 bytes when treated as unsigned to request 9 bytes, even though
all eight bytes were copied into the buffer. What has been omitted is the
zero sign bit -- redundant if the caller's intention is to treat the value
as unsigned.
Specifying ``Py_ASNATIVEBYTES_NATIVE_ENDIAN`` will override any other endian
flags. Passing ``2`` is reserved.
Passing zero to *n_bytes* will return the size of a buffer that would
be large enough to hold the value. This may be larger than technically
necessary, but not unreasonably so.
By default, sufficient buffer will be requested to include a sign bit.
For example, when converting 128 with *n_bytes=1*, the function will return
2 (or more) in order to store a zero sign bit.
If ``Py_ASNATIVEBYTES_UNSIGNED_BUFFER`` is specified, a zero sign bit
will be omitted from size calculations. This allows, for example, 128 to fit
in a single-byte buffer. If the destination buffer is later treated as
signed, a positive input value may become negative.
Note that the flag does not affect handling of negative values: for those,
space for a sign bit is always requested.
Specifying ``Py_ASNATIVEBYTES_REJECT_NEGATIVE`` causes an exception to be set
if *pylong* is negative. Without this flag, negative values will be copied
provided there is enough space for at least one sign bit, regardless of
whether ``Py_ASNATIVEBYTES_UNSIGNED_BUFFER`` was specified.
.. note::
Passing *n_bytes=0* to this function is not an accurate way to determine
the bit length of a value.
With the default *flags* (``-1``, or *UNSIGNED_BUFFER* without
*REJECT_NEGATIVE*), multiple Python integers can map to a single value
without overflow. For example, both ``255`` and ``-1`` fit a single-byte
buffer and set all its bits.
This matches typical C cast behavior.
.. versionadded:: 3.13

View file

@ -411,6 +411,31 @@ The available slot types are:
.. versionadded:: 3.12
.. c:macro:: Py_mod_gil
Specifies one of the following values:
.. c:macro:: Py_MOD_GIL_USED
The module depends on the presence of the global interpreter lock (GIL),
and may access global state without synchronization.
.. c:macro:: Py_MOD_GIL_NOT_USED
The module is safe to run without an active GIL.
This slot is ignored by Python builds not configured with
:option:`--disable-gil`. Otherwise, it determines whether or not importing
this module will cause the GIL to be automatically enabled. See
:envvar:`PYTHON_GIL` and :option:`-X gil <-X>` for more detail.
Multiple ``Py_mod_gil`` slots may not be specified in one module definition.
If ``Py_mod_gil`` is not specified, the import machinery defaults to
``Py_MOD_GIL_USED``.
.. versionadded: 3.13
See :PEP:`489` for more details on multi-phase initialization.
Low-level module creation functions
@ -609,6 +634,19 @@ state:
.. versionadded:: 3.9
.. c:function:: int PyUnstable_Module_SetGIL(PyObject *module, void *gil)
Indicate that *module* does or does not support running without the global
interpreter lock (GIL), using one of the values from
:c:macro:`Py_mod_gil`. It must be called during *module*'s initialization
function. If this function is not called during module initialization, the
import machinery assumes the module does not support running without the
GIL. This function is only available in Python builds configured with
:option:`--disable-gil`.
Return ``-1`` on error, ``0`` on success.
.. versionadded:: 3.13
Module lookup
^^^^^^^^^^^^^

164
Doc/c-api/monitoring.rst Normal file
View file

@ -0,0 +1,164 @@
.. highlight:: c
.. _monitoring:
Monitorong C API
================
Added in version 3.13.
An extension may need to interact with the event monitoring system. Subscribing
to events and registering callbacks can be done via the Python API exposed in
:mod:`sys.monitoring`.
Generating Execution Events
===========================
The functions below make it possible for an extension to fire monitoring
events as it emulates the execution of Python code. Each of these functions
accepts a ``PyMonitoringState`` struct which contains concise information
about the activation state of events, as well as the event arguments, which
include a ``PyObject*`` representing the code object, the instruction offset
and sometimes additional, event-specific arguments (see :mod:`sys.monitoring`
for details about the signatures of the different event callbacks).
The ``codelike`` argument should be an instance of :class:`types.CodeType`
or of a type that emulates it.
The VM disables tracing when firing an event, so there is no need for user
code to do that.
Monitoring functions should not be called with an exception set,
except those listed below as working with the current exception.
.. c:type:: PyMonitoringState
Representation of the state of an event type. It is allocated by the user
while its contents are maintained by the monitoring API functions described below.
All of the functions below return 0 on success and -1 (with an exception set) on error.
See :mod:`sys.monitoring` for descriptions of the events.
.. c:function:: int PyMonitoring_FirePyStartEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset)
Fire a ``PY_START`` event.
.. c:function:: int PyMonitoring_FirePyResumeEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset)
Fire a ``PY_RESUME`` event.
.. c:function:: int PyMonitoring_FirePyReturnEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, PyObject* retval)
Fire a ``PY_RETURN`` event.
.. c:function:: int PyMonitoring_FirePyYieldEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, PyObject* retval)
Fire a ``PY_YIELD`` event.
.. c:function:: int PyMonitoring_FireCallEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, PyObject* callable, PyObject *arg0)
Fire a ``CALL`` event.
.. c:function:: int PyMonitoring_FireLineEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, int lineno)
Fire a ``LINE`` event.
.. c:function:: int PyMonitoring_FireJumpEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, PyObject *target_offset)
Fire a ``JUMP`` event.
.. c:function:: int PyMonitoring_FireBranchEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, PyObject *target_offset)
Fire a ``BRANCH`` event.
.. c:function:: int PyMonitoring_FireCReturnEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, PyObject *retval)
Fire a ``C_RETURN`` event.
.. c:function:: int PyMonitoring_FirePyThrowEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset)
Fire a ``PY_THROW`` event with the current exception (as returned by
:c:func:`PyErr_GetRaisedException`).
.. c:function:: int PyMonitoring_FireRaiseEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset)
Fire a ``RAISE`` event with the current exception (as returned by
:c:func:`PyErr_GetRaisedException`).
.. c:function:: int PyMonitoring_FireCRaiseEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset)
Fire a ``C_RAISE`` event with the current exception (as returned by
:c:func:`PyErr_GetRaisedException`).
.. c:function:: int PyMonitoring_FireReraiseEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset)
Fire a ``RERAISE`` event with the current exception (as returned by
:c:func:`PyErr_GetRaisedException`).
.. c:function:: int PyMonitoring_FireExceptionHandledEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset)
Fire an ``EXCEPTION_HANDLED`` event with the current exception (as returned by
:c:func:`PyErr_GetRaisedException`).
.. c:function:: int PyMonitoring_FirePyUnwindEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset)
Fire a ``PY_UNWIND`` event with the current exception (as returned by
:c:func:`PyErr_GetRaisedException`).
.. c:function:: int PyMonitoring_FireStopIterationEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset)
Fire a ``STOP_ITERATION`` event with the current exception (as returned by
:c:func:`PyErr_GetRaisedException`).
Managing the Monitoring State
-----------------------------
Monitoring states can be managed with the help of monitoring scopes. A scope
would typically correspond to a python function.
.. :c:function:: int PyMonitoring_EnterScope(PyMonitoringState *state_array, uint64_t *version, const uint8_t *event_types, Py_ssize_t length)
Enter a monitored scope. ``event_types`` is an array of the event IDs for
events that may be fired from the scope. For example, the ID of a ``PY_START``
event is the value ``PY_MONITORING_EVENT_PY_START``, which is numerically equal
to the base-2 logarithm of ``sys.monitoring.events.PY_START``.
``state_array`` is an array with a monitoring state entry for each event in
``event_types``, it is allocated by the user but populated by
``PyMonitoring_EnterScope`` with information about the activation state of
the event. The size of ``event_types`` (and hence also of ``state_array``)
is given in ``length``.
The ``version`` argument is a pointer to a value which should be allocated
by the user together with ``state_array`` and initialized to 0,
and then set only by ``PyMonitoring_EnterScope`` itelf. It allows this
function to determine whether event states have changed since the previous call,
and to return quickly if they have not.
The scopes referred to here are lexical scopes: a function, class or method.
``PyMonitoring_EnterScope`` should be called whenever the lexical scope is
entered. Scopes can be reentered, reusing the same *state_array* and *version*,
in situations like when emulating a recursive Python function. When a code-like's
execution is paused, such as when emulating a generator, the scope needs to
be exited and re-entered.
.. :c:function:: int PyMonitoring_ExitScope(void)
Exit the last scope that was entered with ``PyMonitoring_EnterScope``.

View file

@ -65,7 +65,7 @@ Object Protocol
Properly handle returning :c:data:`Py_NotImplemented` from within a C
function (that is, create a new :term:`strong reference`
to NotImplemented and return it).
to :const:`NotImplemented` and return it).
.. c:macro:: Py_PRINT_RAW

View file

@ -72,6 +72,35 @@ with the :term:`GIL` held.
See :func:`time.time` for details important on this clock.
Raw Clock Functions
-------------------
Similar to clock functions, but don't set an exception on error and don't
require the caller to hold the GIL.
On success, the functions return ``0``.
On failure, they set ``*result`` to ``0`` and return ``-1``, *without* setting
an exception. To get the cause of the error, acquire the GIL and call the
regular (non-``Raw``) function. Note that the regular function may succeed after
the ``Raw`` one failed.
.. c:function:: int PyTime_MonotonicRaw(PyTime_t *result)
Similar to :c:func:`PyTime_Monotonic`,
but don't set an exception on error and don't require holding the GIL.
.. c:function:: int PyTime_PerfCounterRaw(PyTime_t *result)
Similar to :c:func:`PyTime_PerfCounter`,
but don't set an exception on error and don't require holding the GIL.
.. c:function:: int PyTime_TimeRaw(PyTime_t *result)
Similar to :c:func:`PyTime_Time`,
but don't set an exception on error and don't require holding the GIL.
Conversion functions
--------------------

View file

@ -59,6 +59,12 @@ Tuple Objects
Return the object at position *pos* in the tuple pointed to by *p*. If *pos* is
negative or out of bounds, return ``NULL`` and set an :exc:`IndexError` exception.
The returned reference is borrowed from the tuple *p*
(that is: it is only valid as long as you hold a reference to *p*).
To get a :term:`strong reference`, use
:c:func:`Py_NewRef(PyTuple_GetItem(...)) <Py_NewRef>`
or :c:func:`PySequence_GetItem`.
.. c:function:: PyObject* PyTuple_GET_ITEM(PyObject *p, Py_ssize_t pos)

View file

@ -883,6 +883,10 @@ and :c:data:`PyType_Type` effectively act as defaults.)
:c:member:`~PyTypeObject.tp_richcompare` and :c:member:`~PyTypeObject.tp_hash`, when the subtype's
:c:member:`~PyTypeObject.tp_richcompare` and :c:member:`~PyTypeObject.tp_hash` are both ``NULL``.
**Default:**
:c:data:`PyBaseObject_Type` uses :c:func:`PyObject_GenericHash`.
.. c:member:: ternaryfunc PyTypeObject.tp_call
@ -1030,7 +1034,8 @@ and :c:data:`PyType_Type` effectively act as defaults.)
the type, and the type object is INCREF'ed when a new instance is created, and
DECREF'ed when an instance is destroyed (this does not apply to instances of
subtypes; only the type referenced by the instance's ob_type gets INCREF'ed or
DECREF'ed).
DECREF'ed). Heap types should also :ref:`support garbage collection <supporting-cycle-detection>`
as they can form a reference cycle with their own module object.
**Inheritance:**
@ -1376,7 +1381,7 @@ and :c:data:`PyType_Type` effectively act as defaults.)
Py_VISIT(Py_TYPE(self));
It is only needed since Python 3.9. To support Python 3.8 and older, this
line must be conditionnal::
line must be conditional::
#if PY_VERSION_HEX >= 0x03090000
Py_VISIT(Py_TYPE(self));

View file

@ -523,7 +523,7 @@ APIs:
- Get the fully qualified name of an object type;
call :c:func:`PyType_GetFullyQualifiedName`.
* - ``T#``
* - ``#T``
- :c:expr:`PyObject*`
- Similar to ``T`` format, but use a colon (``:``) as separator between
the module name and the qualified name.
@ -533,7 +533,7 @@ APIs:
- Get the fully qualified name of a type;
call :c:func:`PyType_GetFullyQualifiedName`.
* - ``N#``
* - ``#N``
- :c:expr:`PyTypeObject*`
- Similar to ``N`` format, but use a colon (``:``) as separator between
the module name and the qualified name.
@ -574,7 +574,7 @@ APIs:
copied as-is to the result string, and any extra arguments discarded.
.. versionchanged:: 3.13
Support for ``%T``, ``%T#``, ``%N`` and ``%N#`` formats added.
Support for ``%T``, ``%#T``, ``%N`` and ``%#N`` formats added.
.. c:function:: PyObject* PyUnicode_FromFormatV(const char *format, va_list vargs)

View file

@ -12,6 +12,8 @@ import time
sys.path.append(os.path.abspath('tools/extensions'))
sys.path.append(os.path.abspath('includes'))
from pyspecific import SOURCE_URI
# General configuration
# ---------------------
@ -24,6 +26,7 @@ extensions = [
'pyspecific',
'sphinx.ext.coverage',
'sphinx.ext.doctest',
'sphinx.ext.extlinks',
]
# Skip if downstream redistributors haven't installed them
@ -128,6 +131,7 @@ nitpick_ignore = [
('c:func', 'vsnprintf'),
# Standard C types
('c:type', 'FILE'),
('c:type', 'int32_t'),
('c:type', 'int64_t'),
('c:type', 'intmax_t'),
('c:type', 'off_t'),
@ -295,8 +299,8 @@ smartquotes_excludes = {
'languages': ['ja', 'fr', 'zh_TW', 'zh_CN'], 'builders': ['man', 'text'],
}
# Avoid a warning with Sphinx >= 2.0
master_doc = 'contents'
# Avoid a warning with Sphinx >= 4.0
root_doc = 'contents'
# Allow translation of index directives
gettext_additional_targets = [
@ -431,6 +435,10 @@ latex_appendices = ['glossary', 'about', 'license', 'copyright']
epub_author = 'Python Documentation Authors'
epub_publisher = 'Python Software Foundation'
# index pages are not valid xhtml
# https://github.com/sphinx-doc/sphinx/issues/12359
epub_use_index = False
# Options for the coverage checker
# --------------------------------
@ -513,6 +521,19 @@ linkcheck_ignore = [
r'https://unix.org/version2/whatsnew/lp64_wp.html',
]
# Options for sphinx.ext.extlinks
# -------------------------------
# This config is a dictionary of external sites,
# mapping unique short aliases to a base URL and a prefix.
# https://www.sphinx-doc.org/en/master/usage/extensions/extlinks.html
extlinks = {
"cve": ("https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-%s", "CVE-%s"),
"cwe": ("https://cwe.mitre.org/data/definitions/%s.html", "CWE-%s"),
"pypi": ("https://pypi.org/project/%s/", "%s"),
"source": (SOURCE_URI, "%s"),
}
extlinks_detect_hardcoded_links = True
# Options for extensions
# ----------------------

View file

@ -188,10 +188,14 @@ function,PyEval_EvalFrame,3.2,,
function,PyEval_EvalFrameEx,3.2,,
function,PyEval_GetBuiltins,3.2,,
function,PyEval_GetFrame,3.2,,
function,PyEval_GetFrameBuiltins,3.13,,
function,PyEval_GetFrameGlobals,3.13,,
function,PyEval_GetFrameLocals,3.13,,
function,PyEval_GetFuncDesc,3.2,,
function,PyEval_GetFuncName,3.2,,
function,PyEval_GetGlobals,3.2,,
function,PyEval_GetLocals,3.2,,
function,PyEval_InitThreads,3.2,,
function,PyEval_ReleaseThread,3.2,,
function,PyEval_RestoreThread,3.2,,
function,PyEval_SaveThread,3.2,,
@ -617,6 +621,8 @@ function,PySys_FormatStdout,3.2,,
function,PySys_GetObject,3.2,,
function,PySys_GetXOptions,3.7,,
function,PySys_ResetWarnOptions,3.2,,
function,PySys_SetArgv,3.2,,
function,PySys_SetArgvEx,3.2,,
function,PySys_SetObject,3.2,,
function,PySys_WriteStderr,3.2,,
function,PySys_WriteStdout,3.2,,
@ -679,6 +685,7 @@ function,PyType_GenericNew,3.2,,
function,PyType_GetFlags,3.2,,
function,PyType_GetFullyQualifiedName,3.13,,
function,PyType_GetModule,3.10,,
function,PyType_GetModuleByDef,3.13,,
function,PyType_GetModuleName,3.13,,
function,PyType_GetModuleState,3.10,,
function,PyType_GetName,3.11,,
@ -867,6 +874,8 @@ function,Py_NewInterpreter,3.2,,
function,Py_NewRef,3.10,,
function,Py_ReprEnter,3.2,,
function,Py_ReprLeave,3.2,,
function,Py_SetProgramName,3.2,,
function,Py_SetPythonHome,3.2,,
function,Py_SetRecursionLimit,3.2,,
type,Py_UCS4,3.2,,
macro,Py_UNBLOCK_THREADS,3.2,,

View file

@ -616,8 +616,7 @@ use ``p.read(n)``.
("ptys") instead of pipes. Or you can use a Python interface to Don Libes'
"expect" library. A Python extension that interfaces to expect is called
"expy" and available from https://expectpy.sourceforge.net. A pure Python
solution that works like expect is `pexpect
<https://pypi.org/project/pexpect/>`_.
solution that works like expect is :pypi:`pexpect`.
How do I access the serial (RS232) port?
@ -625,7 +624,7 @@ How do I access the serial (RS232) port?
For Win32, OSX, Linux, BSD, Jython, IronPython:
https://pypi.org/project/pyserial/
:pypi:`pyserial`
For Unix, see a Usenet post by Mitch Chapman:

View file

@ -9,13 +9,14 @@ Glossary
.. glossary::
``>>>``
The default Python prompt of the interactive shell. Often seen for code
examples which can be executed interactively in the interpreter.
The default Python prompt of the :term:`interactive` shell. Often
seen for code examples which can be executed interactively in the
interpreter.
``...``
Can refer to:
* The default Python prompt of the interactive shell when entering the
* The default Python prompt of the :term:`interactive` shell when entering the
code for an indented code block, when within a pair of matching left and
right delimiters (parentheses, square brackets, curly braces or triple
quotes), or after specifying a decorator.
@ -547,12 +548,12 @@ Glossary
tasks such as compression or hashing. Also, the GIL is always released
when doing I/O.
Past efforts to create a "free-threaded" interpreter (one which locks
shared data at a much finer granularity) have not been successful
because performance suffered in the common single-processor case. It
is believed that overcoming this performance issue would make the
implementation much more complicated and therefore costlier to maintain.
As of Python 3.13, the GIL can be disabled using the :option:`--disable-gil`
build configuration. After building Python with this option, code must be
run with :option:`-X gil 0 <-X>` or after setting the :envvar:`PYTHON_GIL=0 <PYTHON_GIL>`
environment variable. This feature enables improved performance for
multi-threaded applications and makes it easier to use multi-core CPUs
efficiently. For more details, see :pep:`703`.
hash-based pyc
A bytecode cache file that uses the hash rather than the last-modified
@ -620,7 +621,8 @@ Glossary
execute them and see their results. Just launch ``python`` with no
arguments (possibly by selecting it from your computer's main
menu). It is a very powerful way to test out new ideas or inspect
modules and packages (remember ``help(x)``).
modules and packages (remember ``help(x)``). For more on interactive
mode, see :ref:`tut-interac`.
interpreted
Python is an interpreted language, as opposed to a compiled one,
@ -800,8 +802,7 @@ Glossary
method resolution order
Method Resolution Order is the order in which base classes are searched
for a member during lookup. See `The Python 2.3 Method Resolution Order
<https://www.python.org/download/releases/2.3/mro/>`_ for details of the
for a member during lookup. See :ref:`python_2.3_mro` for details of the
algorithm used by the Python interpreter since the 2.3 release.
module
@ -1085,6 +1086,10 @@ Glossary
See also :term:`namespace package`.
REPL
An acronym for the "readevalprint loop", another name for the
:term:`interactive` interpreter shell.
__slots__
A declaration inside a class that saves memory by pre-declaring space for
instance attributes and eliminating instance dictionaries. Though

View file

@ -43,7 +43,7 @@ appearance---and the curses library will figure out what control codes
need to be sent to the terminal to produce the right output. curses
doesn't provide many user-interface concepts such as buttons, checkboxes,
or dialogs; if you need such features, consider a user interface library such as
`Urwid <https://pypi.org/project/urwid/>`_.
:pypi:`Urwid`.
The curses library was originally written for BSD Unix; the later System V
versions of Unix from AT&T added many enhancements and new functions. BSD curses
@ -56,8 +56,7 @@ versions of curses carried by some proprietary Unixes may not support
everything, though.
The Windows version of Python doesn't include the :mod:`curses`
module. A ported version called `UniCurses
<https://pypi.org/project/UniCurses>`_ is available.
module. A ported version called :pypi:`UniCurses` is available.
The Python curses module
@ -429,8 +428,7 @@ User Input
The C curses library offers only very simple input mechanisms. Python's
:mod:`curses` module adds a basic text-input widget. (Other libraries
such as `Urwid <https://pypi.org/project/urwid/>`_ have more extensive
collections of widgets.)
such as :pypi:`Urwid` have more extensive collections of widgets.)
There are two methods for getting input from a window:

View file

@ -33,4 +33,5 @@ Currently, the HOWTOs are:
annotations.rst
isolating-extensions.rst
timerfd.rst
mro.rst

View file

@ -1846,8 +1846,11 @@ the use of a :class:`Filter` does not provide the desired result.
.. _zeromq-handlers:
Subclassing QueueHandler - a ZeroMQ example
-------------------------------------------
Subclassing QueueHandler and QueueListener- a ZeroMQ example
------------------------------------------------------------
Subclass ``QueueHandler``
^^^^^^^^^^^^^^^^^^^^^^^^^
You can use a :class:`QueueHandler` subclass to send messages to other kinds
of queues, for example a ZeroMQ 'publish' socket. In the example below,the
@ -1885,8 +1888,8 @@ data needed by the handler to create the socket::
self.queue.close()
Subclassing QueueListener - a ZeroMQ example
--------------------------------------------
Subclass ``QueueListener``
^^^^^^^^^^^^^^^^^^^^^^^^^^
You can also subclass :class:`QueueListener` to get messages from other kinds
of queues, for example a ZeroMQ 'subscribe' socket. Here's an example::
@ -1903,25 +1906,194 @@ of queues, for example a ZeroMQ 'subscribe' socket. Here's an example::
msg = self.queue.recv_json()
return logging.makeLogRecord(msg)
.. _pynng-handlers:
.. seealso::
Subclassing QueueHandler and QueueListener- a ``pynng`` example
---------------------------------------------------------------
Module :mod:`logging`
API reference for the logging module.
In a similar way to the above section, we can implement a listener and handler
using :pypi:`pynng`, which is a Python binding to
`NNG <https://nng.nanomsg.org/>`_, billed as a spiritual successor to ZeroMQ.
The following snippets illustrate -- you can test them in an environment which has
``pynng`` installed. Just for variety, we present the listener first.
Module :mod:`logging.config`
Configuration API for the logging module.
Module :mod:`logging.handlers`
Useful handlers included with the logging module.
Subclass ``QueueListener``
^^^^^^^^^^^^^^^^^^^^^^^^^^
:ref:`A basic logging tutorial <logging-basic-tutorial>`
.. code-block:: python
:ref:`A more advanced logging tutorial <logging-advanced-tutorial>`
# listener.py
import json
import logging
import logging.handlers
import pynng
DEFAULT_ADDR = "tcp://localhost:13232"
interrupted = False
class NNGSocketListener(logging.handlers.QueueListener):
def __init__(self, uri, /, *handlers, **kwargs):
# Have a timeout for interruptability, and open a
# subscriber socket
socket = pynng.Sub0(listen=uri, recv_timeout=500)
# The b'' subscription matches all topics
topics = kwargs.pop('topics', None) or b''
socket.subscribe(topics)
# We treat the socket as a queue
super().__init__(socket, *handlers, **kwargs)
def dequeue(self, block):
data = None
# Keep looping while not interrupted and no data received over the
# socket
while not interrupted:
try:
data = self.queue.recv(block=block)
break
except pynng.Timeout:
pass
except pynng.Closed: # sometimes happens when you hit Ctrl-C
break
if data is None:
return None
# Get the logging event sent from a publisher
event = json.loads(data.decode('utf-8'))
return logging.makeLogRecord(event)
def enqueue_sentinel(self):
# Not used in this implementation, as the socket isn't really a
# queue
pass
logging.getLogger('pynng').propagate = False
listener = NNGSocketListener(DEFAULT_ADDR, logging.StreamHandler(), topics=b'')
listener.start()
print('Press Ctrl-C to stop.')
try:
while True:
pass
except KeyboardInterrupt:
interrupted = True
finally:
listener.stop()
Subclass ``QueueHandler``
^^^^^^^^^^^^^^^^^^^^^^^^^
.. currentmodule:: logging
.. code-block:: python
# sender.py
import json
import logging
import logging.handlers
import time
import random
import pynng
DEFAULT_ADDR = "tcp://localhost:13232"
class NNGSocketHandler(logging.handlers.QueueHandler):
def __init__(self, uri):
socket = pynng.Pub0(dial=uri, send_timeout=500)
super().__init__(socket)
def enqueue(self, record):
# Send the record as UTF-8 encoded JSON
d = dict(record.__dict__)
data = json.dumps(d)
self.queue.send(data.encode('utf-8'))
def close(self):
self.queue.close()
logging.getLogger('pynng').propagate = False
handler = NNGSocketHandler(DEFAULT_ADDR)
# Make sure the process ID is in the output
logging.basicConfig(level=logging.DEBUG,
handlers=[logging.StreamHandler(), handler],
format='%(levelname)-8s %(name)10s %(process)6s %(message)s')
levels = (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR,
logging.CRITICAL)
logger_names = ('myapp', 'myapp.lib1', 'myapp.lib2')
msgno = 1
while True:
# Just randomly select some loggers and levels and log away
level = random.choice(levels)
logger = logging.getLogger(random.choice(logger_names))
logger.log(level, 'Message no. %5d' % msgno)
msgno += 1
delay = random.random() * 2 + 0.5
time.sleep(delay)
You can run the above two snippets in separate command shells. If we run the
listener in one shell and run the sender in two separate shells, we should see
something like the following. In the first sender shell:
.. code-block:: console
$ python sender.py
DEBUG myapp 613 Message no. 1
WARNING myapp.lib2 613 Message no. 2
CRITICAL myapp.lib2 613 Message no. 3
WARNING myapp.lib2 613 Message no. 4
CRITICAL myapp.lib1 613 Message no. 5
DEBUG myapp 613 Message no. 6
CRITICAL myapp.lib1 613 Message no. 7
INFO myapp.lib1 613 Message no. 8
(and so on)
In the second sender shell:
.. code-block:: console
$ python sender.py
INFO myapp.lib2 657 Message no. 1
CRITICAL myapp.lib2 657 Message no. 2
CRITICAL myapp 657 Message no. 3
CRITICAL myapp.lib1 657 Message no. 4
INFO myapp.lib1 657 Message no. 5
WARNING myapp.lib2 657 Message no. 6
CRITICAL myapp 657 Message no. 7
DEBUG myapp.lib1 657 Message no. 8
(and so on)
In the listener shell:
.. code-block:: console
$ python listener.py
Press Ctrl-C to stop.
DEBUG myapp 613 Message no. 1
WARNING myapp.lib2 613 Message no. 2
INFO myapp.lib2 657 Message no. 1
CRITICAL myapp.lib2 613 Message no. 3
CRITICAL myapp.lib2 657 Message no. 2
CRITICAL myapp 657 Message no. 3
WARNING myapp.lib2 613 Message no. 4
CRITICAL myapp.lib1 613 Message no. 5
CRITICAL myapp.lib1 657 Message no. 4
INFO myapp.lib1 657 Message no. 5
DEBUG myapp 613 Message no. 6
WARNING myapp.lib2 657 Message no. 6
CRITICAL myapp 657 Message no. 7
CRITICAL myapp.lib1 613 Message no. 7
INFO myapp.lib1 613 Message no. 8
DEBUG myapp.lib1 657 Message no. 8
(and so on)
As you can see, the logging from the two sender processes is interleaved in the
listener's output.
An example dictionary-based configuration
-----------------------------------------
@ -3403,9 +3575,8 @@ A Qt GUI for logging
A question that comes up from time to time is about how to log to a GUI
application. The `Qt <https://www.qt.io/>`_ framework is a popular
cross-platform UI framework with Python bindings using `PySide2
<https://pypi.org/project/PySide2/>`_ or `PyQt5
<https://pypi.org/project/PyQt5/>`_ libraries.
cross-platform UI framework with Python bindings using :pypi:`PySide2`
or :pypi:`PyQt5` libraries.
The following example shows how to log to a Qt GUI. This introduces a simple
``QtHandler`` class which takes a callable, which should be a slot in the main
@ -3418,7 +3589,7 @@ The worker thread is implemented using Qt's ``QThread`` class rather than the
:mod:`threading` module, as there are circumstances where one has to use
``QThread``, which offers better integration with other ``Qt`` components.
The code should work with recent releases of either ``PySide6``, ``PyQt6``,
The code should work with recent releases of any of ``PySide6``, ``PyQt6``,
``PySide2`` or ``PyQt5``. You should be able to adapt the approach to earlier
versions of Qt. Please refer to the comments in the code snippet for more
detailed information.

671
Doc/howto/mro.rst Normal file
View file

@ -0,0 +1,671 @@
.. _python_2.3_mro:
The Python 2.3 Method Resolution Order
======================================
.. note::
This is a historical document, provided as an appendix to the official
documentation.
The Method Resolution Order discussed here was *introduced* in Python 2.3,
but it is still used in later versions -- including Python 3.
By `Michele Simionato <https://www.phyast.pitt.edu/~micheles/>`__.
:Abstract:
*This document is intended for Python programmers who want to
understand the C3 Method Resolution Order used in Python 2.3.
Although it is not intended for newbies, it is quite pedagogical with
many worked out examples. I am not aware of other publicly available
documents with the same scope, therefore it should be useful.*
Disclaimer:
*I donate this document to the Python Software Foundation, under the
Python 2.3 license. As usual in these circumstances, I warn the
reader that what follows* should *be correct, but I don't give any
warranty. Use it at your own risk and peril!*
Acknowledgments:
*All the people of the Python mailing list who sent me their support.
Paul Foley who pointed out various imprecisions and made me to add the
part on local precedence ordering. David Goodger for help with the
formatting in reStructuredText. David Mertz for help with the editing.
Finally, Guido van Rossum who enthusiastically added this document to
the official Python 2.3 home-page.*
The beginning
-------------
*Felix qui potuit rerum cognoscere causas* -- Virgilius
Everything started with a post by Samuele Pedroni to the Python
development mailing list [#]_. In his post, Samuele showed that the
Python 2.2 method resolution order is not monotonic and he proposed to
replace it with the C3 method resolution order. Guido agreed with his
arguments and therefore now Python 2.3 uses C3. The C3 method itself
has nothing to do with Python, since it was invented by people working
on Dylan and it is described in a paper intended for lispers [#]_. The
present paper gives a (hopefully) readable discussion of the C3
algorithm for Pythonistas who want to understand the reasons for the
change.
First of all, let me point out that what I am going to say only applies
to the *new style classes* introduced in Python 2.2: *classic classes*
maintain their old method resolution order, depth first and then left to
right. Therefore, there is no breaking of old code for classic classes;
and even if in principle there could be breaking of code for Python 2.2
new style classes, in practice the cases in which the C3 resolution
order differs from the Python 2.2 method resolution order are so rare
that no real breaking of code is expected. Therefore:
*Don't be scared!*
Moreover, unless you make strong use of multiple inheritance and you
have non-trivial hierarchies, you don't need to understand the C3
algorithm, and you can easily skip this paper. On the other hand, if
you really want to know how multiple inheritance works, then this paper
is for you. The good news is that things are not as complicated as you
might expect.
Let me begin with some basic definitions.
1) Given a class C in a complicated multiple inheritance hierarchy, it
is a non-trivial task to specify the order in which methods are
overridden, i.e. to specify the order of the ancestors of C.
2) The list of the ancestors of a class C, including the class itself,
ordered from the nearest ancestor to the furthest, is called the
class precedence list or the *linearization* of C.
3) The *Method Resolution Order* (MRO) is the set of rules that
construct the linearization. In the Python literature, the idiom
"the MRO of C" is also used as a synonymous for the linearization of
the class C.
4) For instance, in the case of single inheritance hierarchy, if C is a
subclass of C1, and C1 is a subclass of C2, then the linearization of
C is simply the list [C, C1 , C2]. However, with multiple
inheritance hierarchies, the construction of the linearization is
more cumbersome, since it is more difficult to construct a
linearization that respects *local precedence ordering* and
*monotonicity*.
5) I will discuss the local precedence ordering later, but I can give
the definition of monotonicity here. A MRO is monotonic when the
following is true: *if C1 precedes C2 in the linearization of C,
then C1 precedes C2 in the linearization of any subclass of C*.
Otherwise, the innocuous operation of deriving a new class could
change the resolution order of methods, potentially introducing very
subtle bugs. Examples where this happens will be shown later.
6) Not all classes admit a linearization. There are cases, in
complicated hierarchies, where it is not possible to derive a class
such that its linearization respects all the desired properties.
Here I give an example of this situation. Consider the hierarchy
>>> O = object
>>> class X(O): pass
>>> class Y(O): pass
>>> class A(X,Y): pass
>>> class B(Y,X): pass
which can be represented with the following inheritance graph, where I
have denoted with O the ``object`` class, which is the beginning of any
hierarchy for new style classes:
.. code-block:: text
-----------
| |
| O |
| / \ |
- X Y /
| / | /
| / |/
A B
\ /
?
In this case, it is not possible to derive a new class C from A and B,
since X precedes Y in A, but Y precedes X in B, therefore the method
resolution order would be ambiguous in C.
Python 2.3 raises an exception in this situation (TypeError: MRO
conflict among bases Y, X) forbidding the naive programmer from creating
ambiguous hierarchies. Python 2.2 instead does not raise an exception,
but chooses an *ad hoc* ordering (CABXYO in this case).
The C3 Method Resolution Order
------------------------------
Let me introduce a few simple notations which will be useful for the
following discussion. I will use the shortcut notation::
C1 C2 ... CN
to indicate the list of classes [C1, C2, ... , CN].
The *head* of the list is its first element::
head = C1
whereas the *tail* is the rest of the list::
tail = C2 ... CN.
I shall also use the notation::
C + (C1 C2 ... CN) = C C1 C2 ... CN
to denote the sum of the lists [C] + [C1, C2, ... ,CN].
Now I can explain how the MRO works in Python 2.3.
Consider a class C in a multiple inheritance hierarchy, with C
inheriting from the base classes B1, B2, ... , BN. We want to
compute the linearization L[C] of the class C. The rule is the
following:
*the linearization of C is the sum of C plus the merge of the
linearizations of the parents and the list of the parents.*
In symbolic notation::
L[C(B1 ... BN)] = C + merge(L[B1] ... L[BN], B1 ... BN)
In particular, if C is the ``object`` class, which has no parents, the
linearization is trivial::
L[object] = object.
However, in general one has to compute the merge according to the following
prescription:
*take the head of the first list, i.e L[B1][0]; if this head is not in
the tail of any of the other lists, then add it to the linearization
of C and remove it from the lists in the merge, otherwise look at the
head of the next list and take it, if it is a good head. Then repeat
the operation until all the class are removed or it is impossible to
find good heads. In this case, it is impossible to construct the
merge, Python 2.3 will refuse to create the class C and will raise an
exception.*
This prescription ensures that the merge operation *preserves* the
ordering, if the ordering can be preserved. On the other hand, if the
order cannot be preserved (as in the example of serious order
disagreement discussed above) then the merge cannot be computed.
The computation of the merge is trivial if C has only one parent
(single inheritance); in this case::
L[C(B)] = C + merge(L[B],B) = C + L[B]
However, in the case of multiple inheritance things are more cumbersome
and I don't expect you can understand the rule without a couple of
examples ;-)
Examples
--------
First example. Consider the following hierarchy:
>>> O = object
>>> class F(O): pass
>>> class E(O): pass
>>> class D(O): pass
>>> class C(D,F): pass
>>> class B(D,E): pass
>>> class A(B,C): pass
In this case the inheritance graph can be drawn as:
.. code-block:: text
6
---
Level 3 | O | (more general)
/ --- \
/ | \ |
/ | \ |
/ | \ |
--- --- --- |
Level 2 3 | D | 4| E | | F | 5 |
--- --- --- |
\ \ _ / | |
\ / \ _ | |
\ / \ | |
--- --- |
Level 1 1 | B | | C | 2 |
--- --- |
\ / |
\ / \ /
---
Level 0 0 | A | (more specialized)
---
The linearizations of O,D,E and F are trivial::
L[O] = O
L[D] = D O
L[E] = E O
L[F] = F O
The linearization of B can be computed as::
L[B] = B + merge(DO, EO, DE)
We see that D is a good head, therefore we take it and we are reduced to
compute ``merge(O,EO,E)``. Now O is not a good head, since it is in the
tail of the sequence EO. In this case the rule says that we have to
skip to the next sequence. Then we see that E is a good head; we take
it and we are reduced to compute ``merge(O,O)`` which gives O. Therefore::
L[B] = B D E O
Using the same procedure one finds::
L[C] = C + merge(DO,FO,DF)
= C + D + merge(O,FO,F)
= C + D + F + merge(O,O)
= C D F O
Now we can compute::
L[A] = A + merge(BDEO,CDFO,BC)
= A + B + merge(DEO,CDFO,C)
= A + B + C + merge(DEO,DFO)
= A + B + C + D + merge(EO,FO)
= A + B + C + D + E + merge(O,FO)
= A + B + C + D + E + F + merge(O,O)
= A B C D E F O
In this example, the linearization is ordered in a pretty nice way
according to the inheritance level, in the sense that lower levels (i.e.
more specialized classes) have higher precedence (see the inheritance
graph). However, this is not the general case.
I leave as an exercise for the reader to compute the linearization for
my second example:
>>> O = object
>>> class F(O): pass
>>> class E(O): pass
>>> class D(O): pass
>>> class C(D,F): pass
>>> class B(E,D): pass
>>> class A(B,C): pass
The only difference with the previous example is the change B(D,E) -->
B(E,D); however even such a little modification completely changes the
ordering of the hierarchy:
.. code-block:: text
6
---
Level 3 | O |
/ --- \
/ | \
/ | \
/ | \
--- --- ---
Level 2 2 | E | 4 | D | | F | 5
--- --- ---
\ / \ /
\ / \ /
\ / \ /
--- ---
Level 1 1 | B | | C | 3
--- ---
\ /
\ /
---
Level 0 0 | A |
---
Notice that the class E, which is in the second level of the hierarchy,
precedes the class C, which is in the first level of the hierarchy, i.e.
E is more specialized than C, even if it is in a higher level.
A lazy programmer can obtain the MRO directly from Python 2.2, since in
this case it coincides with the Python 2.3 linearization. It is enough
to invoke the .mro() method of class A:
>>> A.mro() # doctest: +NORMALIZE_WHITESPACE
[<class 'A'>, <class 'B'>, <class 'E'>,
<class 'C'>, <class 'D'>, <class 'F'>,
<class 'object'>]
Finally, let me consider the example discussed in the first section,
involving a serious order disagreement. In this case, it is
straightforward to compute the linearizations of O, X, Y, A and B:
.. code-block:: text
L[O] = 0
L[X] = X O
L[Y] = Y O
L[A] = A X Y O
L[B] = B Y X O
However, it is impossible to compute the linearization for a class C
that inherits from A and B::
L[C] = C + merge(AXYO, BYXO, AB)
= C + A + merge(XYO, BYXO, B)
= C + A + B + merge(XYO, YXO)
At this point we cannot merge the lists XYO and YXO, since X is in the
tail of YXO whereas Y is in the tail of XYO: therefore there are no
good heads and the C3 algorithm stops. Python 2.3 raises an error and
refuses to create the class C.
Bad Method Resolution Orders
----------------------------
A MRO is *bad* when it breaks such fundamental properties as local
precedence ordering and monotonicity. In this section, I will show
that both the MRO for classic classes and the MRO for new style classes
in Python 2.2 are bad.
It is easier to start with the local precedence ordering. Consider the
following example:
>>> F=type('Food',(),{'remember2buy':'spam'})
>>> E=type('Eggs',(F,),{'remember2buy':'eggs'})
>>> G=type('GoodFood',(F,E),{}) # under Python 2.3 this is an error! # doctest: +SKIP
with inheritance diagram
.. code-block:: text
O
|
(buy spam) F
| \
| E (buy eggs)
| /
G
(buy eggs or spam ?)
We see that class G inherits from F and E, with F *before* E: therefore
we would expect the attribute *G.remember2buy* to be inherited by
*F.rembermer2buy* and not by *E.remember2buy*: nevertheless Python 2.2
gives
>>> G.remember2buy # doctest: +SKIP
'eggs'
This is a breaking of local precedence ordering since the order in the
local precedence list, i.e. the list of the parents of G, is not
preserved in the Python 2.2 linearization of G::
L[G,P22]= G E F object # F *follows* E
One could argue that the reason why F follows E in the Python 2.2
linearization is that F is less specialized than E, since F is the
superclass of E; nevertheless the breaking of local precedence ordering
is quite non-intuitive and error prone. This is particularly true since
it is a different from old style classes:
>>> class F: remember2buy='spam'
>>> class E(F): remember2buy='eggs'
>>> class G(F,E): pass # doctest: +SKIP
>>> G.remember2buy # doctest: +SKIP
'spam'
In this case the MRO is GFEF and the local precedence ordering is
preserved.
As a general rule, hierarchies such as the previous one should be
avoided, since it is unclear if F should override E or viceversa.
Python 2.3 solves the ambiguity by raising an exception in the creation
of class G, effectively stopping the programmer from generating
ambiguous hierarchies. The reason for that is that the C3 algorithm
fails when the merge::
merge(FO,EFO,FE)
cannot be computed, because F is in the tail of EFO and E is in the tail
of FE.
The real solution is to design a non-ambiguous hierarchy, i.e. to derive
G from E and F (the more specific first) and not from F and E; in this
case the MRO is GEF without any doubt.
.. code-block:: text
O
|
F (spam)
/ |
(eggs) E |
\ |
G
(eggs, no doubt)
Python 2.3 forces the programmer to write good hierarchies (or, at
least, less error-prone ones).
On a related note, let me point out that the Python 2.3 algorithm is
smart enough to recognize obvious mistakes, as the duplication of
classes in the list of parents:
>>> class A(object): pass
>>> class C(A,A): pass # error
Traceback (most recent call last):
File "<stdin>", line 1, in ?
TypeError: duplicate base class A
Python 2.2 (both for classic classes and new style classes) in this
situation, would not raise any exception.
Finally, I would like to point out two lessons we have learned from this
example:
1. despite the name, the MRO determines the resolution order of
attributes, not only of methods;
2. the default food for Pythonistas is spam ! (but you already knew
that ;-)
Having discussed the issue of local precedence ordering, let me now
consider the issue of monotonicity. My goal is to show that neither the
MRO for classic classes nor that for Python 2.2 new style classes is
monotonic.
To prove that the MRO for classic classes is non-monotonic is rather
trivial, it is enough to look at the diamond diagram:
.. code-block:: text
C
/ \
/ \
A B
\ /
\ /
D
One easily discerns the inconsistency::
L[B,P21] = B C # B precedes C : B's methods win
L[D,P21] = D A C B C # B follows C : C's methods win!
On the other hand, there are no problems with the Python 2.2 and 2.3
MROs, they give both::
L[D] = D A B C
Guido points out in his essay [#]_ that the classic MRO is not so bad in
practice, since one can typically avoids diamonds for classic classes.
But all new style classes inherit from ``object``, therefore diamonds are
unavoidable and inconsistencies shows up in every multiple inheritance
graph.
The MRO of Python 2.2 makes breaking monotonicity difficult, but not
impossible. The following example, originally provided by Samuele
Pedroni, shows that the MRO of Python 2.2 is non-monotonic:
>>> class A(object): pass
>>> class B(object): pass
>>> class C(object): pass
>>> class D(object): pass
>>> class E(object): pass
>>> class K1(A,B,C): pass
>>> class K2(D,B,E): pass
>>> class K3(D,A): pass
>>> class Z(K1,K2,K3): pass
Here are the linearizations according to the C3 MRO (the reader should
verify these linearizations as an exercise and draw the inheritance
diagram ;-) ::
L[A] = A O
L[B] = B O
L[C] = C O
L[D] = D O
L[E] = E O
L[K1]= K1 A B C O
L[K2]= K2 D B E O
L[K3]= K3 D A O
L[Z] = Z K1 K2 K3 D A B C E O
Python 2.2 gives exactly the same linearizations for A, B, C, D, E, K1,
K2 and K3, but a different linearization for Z::
L[Z,P22] = Z K1 K3 A K2 D B C E O
It is clear that this linearization is *wrong*, since A comes before D
whereas in the linearization of K3 A comes *after* D. In other words, in
K3 methods derived by D override methods derived by A, but in Z, which
still is a subclass of K3, methods derived by A override methods derived
by D! This is a violation of monotonicity. Moreover, the Python 2.2
linearization of Z is also inconsistent with local precedence ordering,
since the local precedence list of the class Z is [K1, K2, K3] (K2
precedes K3), whereas in the linearization of Z K2 *follows* K3. These
problems explain why the 2.2 rule has been dismissed in favor of the C3
rule.
The end
-------
This section is for the impatient reader, who skipped all the previous
sections and jumped immediately to the end. This section is for the
lazy programmer too, who didn't want to exercise her/his brain.
Finally, it is for the programmer with some hubris, otherwise s/he would
not be reading a paper on the C3 method resolution order in multiple
inheritance hierarchies ;-) These three virtues taken all together (and
*not* separately) deserve a prize: the prize is a short Python 2.2
script that allows you to compute the 2.3 MRO without risk to your
brain. Simply change the last line to play with the various examples I
have discussed in this paper.::
#<mro.py>
"""C3 algorithm by Samuele Pedroni (with readability enhanced by me)."""
class __metaclass__(type):
"All classes are metamagically modified to be nicely printed"
__repr__ = lambda cls: cls.__name__
class ex_2:
"Serious order disagreement" #From Guido
class O: pass
class X(O): pass
class Y(O): pass
class A(X,Y): pass
class B(Y,X): pass
try:
class Z(A,B): pass #creates Z(A,B) in Python 2.2
except TypeError:
pass # Z(A,B) cannot be created in Python 2.3
class ex_5:
"My first example"
class O: pass
class F(O): pass
class E(O): pass
class D(O): pass
class C(D,F): pass
class B(D,E): pass
class A(B,C): pass
class ex_6:
"My second example"
class O: pass
class F(O): pass
class E(O): pass
class D(O): pass
class C(D,F): pass
class B(E,D): pass
class A(B,C): pass
class ex_9:
"Difference between Python 2.2 MRO and C3" #From Samuele
class O: pass
class A(O): pass
class B(O): pass
class C(O): pass
class D(O): pass
class E(O): pass
class K1(A,B,C): pass
class K2(D,B,E): pass
class K3(D,A): pass
class Z(K1,K2,K3): pass
def merge(seqs):
print '\n\nCPL[%s]=%s' % (seqs[0][0],seqs),
res = []; i=0
while 1:
nonemptyseqs=[seq for seq in seqs if seq]
if not nonemptyseqs: return res
i+=1; print '\n',i,'round: candidates...',
for seq in nonemptyseqs: # find merge candidates among seq heads
cand = seq[0]; print ' ',cand,
nothead=[s for s in nonemptyseqs if cand in s[1:]]
if nothead: cand=None #reject candidate
else: break
if not cand: raise "Inconsistent hierarchy"
res.append(cand)
for seq in nonemptyseqs: # remove cand
if seq[0] == cand: del seq[0]
def mro(C):
"Compute the class precedence list (mro) according to C3"
return merge([[C]]+map(mro,C.__bases__)+[list(C.__bases__)])
def print_mro(C):
print '\nMRO[%s]=%s' % (C,mro(C))
print '\nP22 MRO[%s]=%s' % (C,C.mro())
print_mro(ex_9.Z)
#</mro.py>
That's all folks,
enjoy !
Resources
---------
.. [#] The thread on python-dev started by Samuele Pedroni:
https://mail.python.org/pipermail/python-dev/2002-October/029035.html
.. [#] The paper *A Monotonic Superclass Linearization for Dylan*:
https://doi.org/10.1145/236337.236343
.. [#] Guido van Rossum's essay, *Unifying types and classes in Python 2.2*:
https://web.archive.org/web/20140210194412/http://www.python.org/download/releases/2.2.2/descrintro

View file

@ -162,12 +162,12 @@ the :option:`!-X` option takes precedence over the environment variable.
Example, using the environment variable::
$ PYTHONPERFSUPPORT=1 python script.py
$ PYTHONPERFSUPPORT=1 perf record -F 9999 -g -o perf.data python script.py
$ perf report -g -i perf.data
Example, using the :option:`!-X` option::
$ python -X perf script.py
$ perf record -F 9999 -g -o perf.data python -X perf script.py
$ perf report -g -i perf.data
Example, using the :mod:`sys` APIs in file :file:`example.py`:
@ -184,7 +184,7 @@ Example, using the :mod:`sys` APIs in file :file:`example.py`:
...then::
$ python ./example.py
$ perf record -F 9999 -g -o perf.data python ./example.py
$ perf report -g -i perf.data
@ -205,3 +205,62 @@ You can check if your system has been compiled with this flag by running::
If you don't see any output it means that your interpreter has not been compiled with
frame pointers and therefore it may not be able to show Python functions in the output
of ``perf``.
How to work without frame pointers
----------------------------------
If you are working with a Python interpreter that has been compiled without
frame pointers, you can still use the ``perf`` profiler, but the overhead will be
a bit higher because Python needs to generate unwinding information for every
Python function call on the fly. Additionally, ``perf`` will take more time to
process the data because it will need to use the DWARF debugging information to
unwind the stack and this is a slow process.
To enable this mode, you can use the environment variable
:envvar:`PYTHON_PERF_JIT_SUPPORT` or the :option:`-X perf_jit <-X>` option,
which will enable the JIT mode for the ``perf`` profiler.
.. note::
Due to a bug in the ``perf`` tool, only ``perf`` versions higher than v6.8
will work with the JIT mode. The fix was also backported to the v6.7.2
version of the tool.
Note that when checking the version of the ``perf`` tool (which can be done
by running ``perf version``) you must take into account that some distros
add some custom version numbers including a ``-`` character. This means
that ``perf 6.7-3`` is not necessarily ``perf 6.7.3``.
When using the perf JIT mode, you need an extra step before you can run ``perf
report``. You need to call the ``perf inject`` command to inject the JIT
information into the ``perf.data`` file.::
$ perf record -F 9999 -g --call-graph dwarf -o perf.data python -Xperf_jit my_script.py
$ perf inject -i perf.data --jit --output perf.jit.data
$ perf report -g -i perf.jit.data
or using the environment variable::
$ PYTHON_PERF_JIT_SUPPORT=1 perf record -F 9999 -g --call-graph dwarf -o perf.data python my_script.py
$ perf inject -i perf.data --jit --output perf.jit.data
$ perf report -g -i perf.jit.data
``perf inject --jit`` command will read ``perf.data``,
automatically pick up the perf dump file that Python creates (in
``/tmp/perf-$PID.dump``), and then create ``perf.jit.data`` which merges all the
JIT information together. It should also create a lot of ``jitted-XXXX-N.so``
files in the current directory which are ELF images for all the JIT trampolines
that were created by Python.
.. warning::
Notice that when using ``--call-graph dwarf`` the ``perf`` tool will take
snapshots of the stack of the process being profiled and save the
information in the ``perf.data`` file. By default the size of the stack dump
is 8192 bytes but the user can change the size by passing the size after
comma like ``--call-graph dwarf,4096``. The size of the stack dump is
important because if the size is too small ``perf`` will not be able to
unwind the stack and the output will be incomplete. On the other hand, if
the size is too big, then ``perf`` won't be able to sample the process as
frequently as it would like as the overhead will be higher.

View file

@ -108,7 +108,7 @@ descriptors to wait until the file descriptor is ready for reading:
# In 1.5 seconds, 1st timer, 2nd timer and 3rd timer fires at once.
#
# If a timer file descriptor is signaled more than once since
# the last os.read() call, os.read() returns the nubmer of signaled
# the last os.read() call, os.read() returns the number of signaled
# as host order of class bytes.
print(f"Signaled events={events}")
for fd, event in events:

View file

@ -594,5 +594,5 @@ This document was reviewed and revised by John Lee.
scripts with a localhost server, I have to prevent urllib from using
the proxy.
.. [#] urllib opener for SSL proxy (CONNECT method): `ASPN Cookbook Recipe
<https://code.activestate.com/recipes/456195/>`_.
<https://code.activestate.com/recipes/456195-urrlib2-opener-for-ssl-proxy-connect-method/>`_.

View file

@ -53,7 +53,7 @@ must be running an SMTP server.
# Guess the content type based on the file's extension. Encoding
# will be ignored, although we should check for simple things like
# gzip'd or compressed files.
ctype, encoding = mimetypes.guess_type(path)
ctype, encoding = mimetypes.guess_file_type(path)
if ctype is None or encoding is not None:
# No guess could be made, or the file is encoded (compressed), so
# use a generic bag-of-bits type.

View file

@ -0,0 +1,8 @@
.. include for modules that don't work on WASM or iOS
.. availability:: not WASI, not iOS.
This module does not work or is not available on WebAssembly platforms, or
on iOS. See :ref:`wasm-availability` for more information on WASM
availability; see :ref:`iOS-availability` for more information on iOS
availability.

View file

@ -1,7 +1,6 @@
.. include for modules that don't work on WASM
.. availability:: not Emscripten, not WASI.
.. availability:: not WASI.
This module does not work or is not available on WebAssembly platforms
``wasm32-emscripten`` and ``wasm32-wasi``. See
This module does not work or is not available on WebAssembly. See
:ref:`wasm-availability` for more information.

View file

@ -1,5 +1,5 @@
:mod:`__future__` --- Future statement definitions
==================================================
:mod:`!__future__` --- Future statement definitions
===================================================
.. module:: __future__
:synopsis: Future statement definitions

View file

@ -1,5 +1,5 @@
:mod:`__main__` --- Top-level code environment
==============================================
:mod:`!__main__` --- Top-level code environment
===============================================
.. module:: __main__
:synopsis: The environment where top-level code is run. Covers command-line

View file

@ -1,5 +1,5 @@
:mod:`_thread` --- Low-level threading API
==========================================
:mod:`!_thread` --- Low-level threading API
===========================================
.. module:: _thread
:synopsis: Low-level threading API.
@ -169,14 +169,14 @@ Lock objects have the following methods:
time can acquire a lock --- that's their reason for existence).
If the *blocking* argument is present, the action depends on its
value: if it is False, the lock is only acquired if it can be acquired
immediately without waiting, while if it is True, the lock is acquired
value: if it is false, the lock is only acquired if it can be acquired
immediately without waiting, while if it is true, the lock is acquired
unconditionally as above.
If the floating-point *timeout* argument is present and positive, it
specifies the maximum wait time in seconds before returning. A negative
*timeout* argument specifies an unbounded wait. You cannot specify
a *timeout* if *blocking* is False.
a *timeout* if *blocking* is false.
The return value is ``True`` if the lock is acquired successfully,
``False`` if not.

View file

@ -1,5 +1,5 @@
:mod:`abc` --- Abstract Base Classes
====================================
:mod:`!abc` --- Abstract Base Classes
=====================================
.. module:: abc
:synopsis: Abstract base classes according to :pep:`3119`.

View file

@ -16,7 +16,6 @@ but they are available on most other systems as well. Here's an overview:
io.rst
time.rst
argparse.rst
getopt.rst
logging.rst
logging.config.rst
logging.handlers.rst

View file

@ -1,5 +1,5 @@
:mod:`argparse` --- Parser for command-line options, arguments and sub-commands
===============================================================================
:mod:`!argparse` --- Parser for command-line options, arguments and sub-commands
================================================================================
.. module:: argparse
:synopsis: Command-line option and argument parsing library.

View file

@ -1,5 +1,5 @@
:mod:`array` --- Efficient arrays of numeric values
===================================================
:mod:`!array` --- Efficient arrays of numeric values
====================================================
.. module:: array
:synopsis: Space efficient arrays of uniformly typed numeric values.

View file

@ -1,5 +1,5 @@
:mod:`ast` --- Abstract Syntax Trees
====================================
:mod:`!ast` --- Abstract Syntax Trees
=====================================
.. module:: ast
:synopsis: Abstract Syntax Tree classes and manipulation.
@ -61,7 +61,7 @@ Node classes
.. attribute:: _fields
Each concrete class has an attribute :attr:`_fields` which gives the names
Each concrete class has an attribute :attr:`!_fields` which gives the names
of all child nodes.
Each instance of a concrete class has one attribute for each child node,
@ -74,6 +74,18 @@ Node classes
as Python lists. All possible attributes must be present and have valid
values when compiling an AST with :func:`compile`.
.. attribute:: _field_types
The :attr:`!_field_types` attribute on each concrete class is a dictionary
mapping field names (as also listed in :attr:`_fields`) to their types.
.. doctest::
>>> ast.TypeVar._field_types
{'name': <class 'str'>, 'bound': ast.expr | None, 'default_value': ast.expr | None}
.. versionadded:: 3.13
.. attribute:: lineno
col_offset
end_lineno
@ -108,7 +120,8 @@ Node classes
If a field that is optional in the grammar is omitted from the constructor,
it defaults to ``None``. If a list field is omitted, it defaults to the empty
list. If any other field is omitted, a :exc:`DeprecationWarning` is raised
list. If a field of type :class:`!ast.expr_context` is omitted, it defaults to
:class:`Load() <ast.Load>`. If any other field is omitted, a :exc:`DeprecationWarning` is raised
and the AST node will not have this field. In Python 3.15, this condition will
raise an error.
@ -173,8 +186,7 @@ Root nodes
Assign(
targets=[
Name(id='x', ctx=Store())],
value=Constant(value=1))],
type_ignores=[])
value=Constant(value=1))])
.. class:: Expression(body)
@ -302,8 +314,7 @@ Literals
value=Call(
func=Name(id='sin', ctx=Load()),
args=[
Name(id='a', ctx=Load())],
keywords=[]),
Name(id='a', ctx=Load())]),
conversion=-1,
format_spec=JoinedStr(
values=[
@ -398,8 +409,7 @@ Variables
Module(
body=[
Expr(
value=Name(id='a', ctx=Load()))],
type_ignores=[])
value=Name(id='a', ctx=Load()))])
>>> print(ast.dump(ast.parse('a = 1'), indent=4))
Module(
@ -407,16 +417,14 @@ Variables
Assign(
targets=[
Name(id='a', ctx=Store())],
value=Constant(value=1))],
type_ignores=[])
value=Constant(value=1))])
>>> print(ast.dump(ast.parse('del a'), indent=4))
Module(
body=[
Delete(
targets=[
Name(id='a', ctx=Del())])],
type_ignores=[])
Name(id='a', ctx=Del())])])
.. class:: Starred(value, ctx)
@ -439,8 +447,7 @@ Variables
value=Name(id='b', ctx=Store()),
ctx=Store())],
ctx=Store())],
value=Name(id='it', ctx=Load()))],
type_ignores=[])
value=Name(id='it', ctx=Load()))])
.. _ast-expressions:
@ -463,8 +470,7 @@ Expressions
Expr(
value=UnaryOp(
op=USub(),
operand=Name(id='a', ctx=Load())))],
type_ignores=[])
operand=Name(id='a', ctx=Load())))])
.. class:: UnaryOp(op, operand)
@ -591,8 +597,7 @@ Expressions
* ``keywords`` holds a list of :class:`.keyword` objects representing
arguments passed by keyword.
When creating a ``Call`` node, ``args`` and ``keywords`` are required, but
they can be empty lists.
The ``args`` and ``keywords`` arguments are optional and default to empty lists.
.. doctest::
@ -729,7 +734,10 @@ Comprehensions
.. doctest::
>>> print(ast.dump(ast.parse('[x for x in numbers]', mode='eval'), indent=4))
>>> print(ast.dump(
... ast.parse('[x for x in numbers]', mode='eval'),
... indent=4,
... ))
Expression(
body=ListComp(
elt=Name(id='x', ctx=Load()),
@ -737,9 +745,11 @@ Comprehensions
comprehension(
target=Name(id='x', ctx=Store()),
iter=Name(id='numbers', ctx=Load()),
ifs=[],
is_async=0)]))
>>> print(ast.dump(ast.parse('{x: x**2 for x in numbers}', mode='eval'), indent=4))
>>> print(ast.dump(
... ast.parse('{x: x**2 for x in numbers}', mode='eval'),
... indent=4,
... ))
Expression(
body=DictComp(
key=Name(id='x', ctx=Load()),
@ -751,9 +761,11 @@ Comprehensions
comprehension(
target=Name(id='x', ctx=Store()),
iter=Name(id='numbers', ctx=Load()),
ifs=[],
is_async=0)]))
>>> print(ast.dump(ast.parse('{x for x in numbers}', mode='eval'), indent=4))
>>> print(ast.dump(
... ast.parse('{x for x in numbers}', mode='eval'),
... indent=4,
... ))
Expression(
body=SetComp(
elt=Name(id='x', ctx=Load()),
@ -761,7 +773,6 @@ Comprehensions
comprehension(
target=Name(id='x', ctx=Store()),
iter=Name(id='numbers', ctx=Load()),
ifs=[],
is_async=0)]))
@ -784,18 +795,15 @@ Comprehensions
elt=Call(
func=Name(id='ord', ctx=Load()),
args=[
Name(id='c', ctx=Load())],
keywords=[]),
Name(id='c', ctx=Load())]),
generators=[
comprehension(
target=Name(id='line', ctx=Store()),
iter=Name(id='file', ctx=Load()),
ifs=[],
is_async=0),
comprehension(
target=Name(id='c', ctx=Store()),
iter=Name(id='line', ctx=Load()),
ifs=[],
is_async=0)]))
>>> print(ast.dump(ast.parse('(n**2 for n in it if n>5 if n<10)', mode='eval'),
@ -834,7 +842,6 @@ Comprehensions
comprehension(
target=Name(id='i', ctx=Store()),
iter=Name(id='soc', ctx=Load()),
ifs=[],
is_async=1)]))
@ -864,8 +871,7 @@ Statements
targets=[
Name(id='a', ctx=Store()),
Name(id='b', ctx=Store())],
value=Constant(value=1))],
type_ignores=[])
value=Constant(value=1))])
>>> print(ast.dump(ast.parse('a,b = c'), indent=4)) # Unpacking
Module(
@ -877,8 +883,7 @@ Statements
Name(id='a', ctx=Store()),
Name(id='b', ctx=Store())],
ctx=Store())],
value=Name(id='c', ctx=Load()))],
type_ignores=[])
value=Name(id='c', ctx=Load()))])
.. class:: AnnAssign(target, annotation, value, simple)
@ -898,8 +903,7 @@ Statements
AnnAssign(
target=Name(id='c', ctx=Store()),
annotation=Name(id='int', ctx=Load()),
simple=1)],
type_ignores=[])
simple=1)])
>>> print(ast.dump(ast.parse('(a): int = 1'), indent=4)) # Annotation with parenthesis
Module(
@ -908,8 +912,7 @@ Statements
target=Name(id='a', ctx=Store()),
annotation=Name(id='int', ctx=Load()),
value=Constant(value=1),
simple=0)],
type_ignores=[])
simple=0)])
>>> print(ast.dump(ast.parse('a.b: int'), indent=4)) # Attribute annotation
Module(
@ -920,8 +923,7 @@ Statements
attr='b',
ctx=Store()),
annotation=Name(id='int', ctx=Load()),
simple=0)],
type_ignores=[])
simple=0)])
>>> print(ast.dump(ast.parse('a[1]: int'), indent=4)) # Subscript annotation
Module(
@ -932,8 +934,7 @@ Statements
slice=Constant(value=1),
ctx=Store()),
annotation=Name(id='int', ctx=Load()),
simple=0)],
type_ignores=[])
simple=0)])
.. class:: AugAssign(target, op, value)
@ -954,8 +955,7 @@ Statements
AugAssign(
target=Name(id='x', ctx=Store()),
op=Add(),
value=Constant(value=2))],
type_ignores=[])
value=Constant(value=2))])
.. class:: Raise(exc, cause)
@ -971,8 +971,7 @@ Statements
body=[
Raise(
exc=Name(id='x', ctx=Load()),
cause=Name(id='y', ctx=Load()))],
type_ignores=[])
cause=Name(id='y', ctx=Load()))])
.. class:: Assert(test, msg)
@ -987,8 +986,7 @@ Statements
body=[
Assert(
test=Name(id='x', ctx=Load()),
msg=Name(id='y', ctx=Load()))],
type_ignores=[])
msg=Name(id='y', ctx=Load()))])
.. class:: Delete(targets)
@ -1005,8 +1003,7 @@ Statements
targets=[
Name(id='x', ctx=Del()),
Name(id='y', ctx=Del()),
Name(id='z', ctx=Del())])],
type_ignores=[])
Name(id='z', ctx=Del())])])
.. class:: Pass()
@ -1018,8 +1015,7 @@ Statements
>>> print(ast.dump(ast.parse('pass'), indent=4))
Module(
body=[
Pass()],
type_ignores=[])
Pass()])
.. class:: TypeAlias(name, type_params, value)
@ -1036,9 +1032,7 @@ Statements
body=[
TypeAlias(
name=Name(id='Alias', ctx=Store()),
type_params=[],
value=Name(id='int', ctx=Load()))],
type_ignores=[])
value=Name(id='int', ctx=Load()))])
.. versionadded:: 3.12
@ -1061,8 +1055,7 @@ Imports
names=[
alias(name='x'),
alias(name='y'),
alias(name='z')])],
type_ignores=[])
alias(name='z')])])
.. class:: ImportFrom(module, names, level)
@ -1083,8 +1076,7 @@ Imports
alias(name='x'),
alias(name='y'),
alias(name='z')],
level=0)],
type_ignores=[])
level=0)])
.. class:: alias(name, asname)
@ -1102,8 +1094,7 @@ Imports
names=[
alias(name='a', asname='b'),
alias(name='c')],
level=2)],
type_ignores=[])
level=2)])
Control flow
^^^^^^^^^^^^
@ -1146,8 +1137,7 @@ Control flow
value=Constant(value=Ellipsis))],
orelse=[
Expr(
value=Constant(value=Ellipsis))])])],
type_ignores=[])
value=Constant(value=Ellipsis))])])])
.. class:: For(target, iter, body, orelse, type_comment)
@ -1181,8 +1171,7 @@ Control flow
value=Constant(value=Ellipsis))],
orelse=[
Expr(
value=Constant(value=Ellipsis))])],
type_ignores=[])
value=Constant(value=Ellipsis))])])
.. class:: While(test, body, orelse)
@ -1207,8 +1196,7 @@ Control flow
value=Constant(value=Ellipsis))],
orelse=[
Expr(
value=Constant(value=Ellipsis))])],
type_ignores=[])
value=Constant(value=Ellipsis))])])
.. class:: Break
@ -1242,9 +1230,7 @@ Control flow
body=[
Break()],
orelse=[
Continue()])],
orelse=[])],
type_ignores=[])
Continue()])])])
.. class:: Try(body, handlers, orelse, finalbody)
@ -1289,8 +1275,7 @@ Control flow
value=Constant(value=Ellipsis))],
finalbody=[
Expr(
value=Constant(value=Ellipsis))])],
type_ignores=[])
value=Constant(value=Ellipsis))])])
.. class:: TryStar(body, handlers, orelse, finalbody)
@ -1318,10 +1303,7 @@ Control flow
type=Name(id='Exception', ctx=Load()),
body=[
Expr(
value=Constant(value=Ellipsis))])],
orelse=[],
finalbody=[])],
type_ignores=[])
value=Constant(value=Ellipsis))])])])
.. versionadded:: 3.11
@ -1353,10 +1335,7 @@ Control flow
ExceptHandler(
type=Name(id='TypeError', ctx=Load()),
body=[
Pass()])],
orelse=[],
finalbody=[])],
type_ignores=[])
Pass()])])])
.. class:: With(items, body, type_comment)
@ -1398,9 +1377,7 @@ Control flow
func=Name(id='something', ctx=Load()),
args=[
Name(id='b', ctx=Load()),
Name(id='d', ctx=Load())],
keywords=[]))])],
type_ignores=[])
Name(id='d', ctx=Load())]))])])
Pattern matching
@ -1457,14 +1434,10 @@ Pattern matching
value=Constant(value=Ellipsis))]),
match_case(
pattern=MatchClass(
cls=Name(id='tuple', ctx=Load()),
patterns=[],
kwd_attrs=[],
kwd_patterns=[]),
cls=Name(id='tuple', ctx=Load())),
body=[
Expr(
value=Constant(value=Ellipsis))])])],
type_ignores=[])
value=Constant(value=Ellipsis))])])])
.. versionadded:: 3.10
@ -1492,8 +1465,7 @@ Pattern matching
value=Constant(value='Relevant')),
body=[
Expr(
value=Constant(value=Ellipsis))])])],
type_ignores=[])
value=Constant(value=Ellipsis))])])])
.. versionadded:: 3.10
@ -1519,8 +1491,7 @@ Pattern matching
pattern=MatchSingleton(value=None),
body=[
Expr(
value=Constant(value=Ellipsis))])])],
type_ignores=[])
value=Constant(value=Ellipsis))])])])
.. versionadded:: 3.10
@ -1552,8 +1523,7 @@ Pattern matching
value=Constant(value=2))]),
body=[
Expr(
value=Constant(value=Ellipsis))])])],
type_ignores=[])
value=Constant(value=Ellipsis))])])])
.. versionadded:: 3.10
@ -1594,8 +1564,7 @@ Pattern matching
MatchStar()]),
body=[
Expr(
value=Constant(value=Ellipsis))])])],
type_ignores=[])
value=Constant(value=Ellipsis))])])])
.. versionadded:: 3.10
@ -1639,11 +1608,10 @@ Pattern matching
Expr(
value=Constant(value=Ellipsis))]),
match_case(
pattern=MatchMapping(keys=[], patterns=[], rest='rest'),
pattern=MatchMapping(rest='rest'),
body=[
Expr(
value=Constant(value=Ellipsis))])])],
type_ignores=[])
value=Constant(value=Ellipsis))])])])
.. versionadded:: 3.10
@ -1685,16 +1653,13 @@ Pattern matching
MatchValue(
value=Constant(value=0)),
MatchValue(
value=Constant(value=0))],
kwd_attrs=[],
kwd_patterns=[]),
value=Constant(value=0))]),
body=[
Expr(
value=Constant(value=Ellipsis))]),
match_case(
pattern=MatchClass(
cls=Name(id='Point3D', ctx=Load()),
patterns=[],
kwd_attrs=[
'x',
'y',
@ -1708,8 +1673,7 @@ Pattern matching
value=Constant(value=0))]),
body=[
Expr(
value=Constant(value=Ellipsis))])])],
type_ignores=[])
value=Constant(value=Ellipsis))])])])
.. versionadded:: 3.10
@ -1751,8 +1715,7 @@ Pattern matching
pattern=MatchAs(),
body=[
Expr(
value=Constant(value=Ellipsis))])])],
type_ignores=[])
value=Constant(value=Ellipsis))])])])
.. versionadded:: 3.10
@ -1785,8 +1748,7 @@ Pattern matching
MatchAs(name='y')]),
body=[
Expr(
value=Constant(value=Ellipsis))])])],
type_ignores=[])
value=Constant(value=Ellipsis))])])])
.. versionadded:: 3.10
@ -1798,15 +1760,17 @@ Type parameters
:ref:`Type parameters <type-params>` can exist on classes, functions, and type
aliases.
.. class:: TypeVar(name, bound)
.. class:: TypeVar(name, bound, default_value)
A :class:`typing.TypeVar`. ``name`` is the name of the type variable.
``bound`` is the bound or constraints, if any. If ``bound`` is a :class:`Tuple`,
it represents constraints; otherwise it represents the bound.
A :class:`typing.TypeVar`. *name* is the name of the type variable.
*bound* is the bound or constraints, if any. If *bound* is a :class:`Tuple`,
it represents constraints; otherwise it represents the bound. *default_value*
is the default value; if the :class:`!TypeVar` has no default, this
attribute will be set to ``None``.
.. doctest::
>>> print(ast.dump(ast.parse("type Alias[T: int] = list[T]"), indent=4))
>>> print(ast.dump(ast.parse("type Alias[T: int = bool] = list[T]"), indent=4))
Module(
body=[
TypeAlias(
@ -1814,28 +1778,39 @@ aliases.
type_params=[
TypeVar(
name='T',
bound=Name(id='int', ctx=Load()))],
bound=Name(id='int', ctx=Load()),
default_value=Name(id='bool', ctx=Load()))],
value=Subscript(
value=Name(id='list', ctx=Load()),
slice=Name(id='T', ctx=Load()),
ctx=Load()))],
type_ignores=[])
ctx=Load()))])
.. versionadded:: 3.12
.. class:: ParamSpec(name)
.. versionchanged:: 3.13
Added the *default_value* parameter.
A :class:`typing.ParamSpec`. ``name`` is the name of the parameter specification.
.. class:: ParamSpec(name, default_value)
A :class:`typing.ParamSpec`. *name* is the name of the parameter specification.
*default_value* is the default value; if the :class:`!ParamSpec` has no default,
this attribute will be set to ``None``.
.. doctest::
>>> print(ast.dump(ast.parse("type Alias[**P] = Callable[P, int]"), indent=4))
>>> print(ast.dump(ast.parse("type Alias[**P = (int, str)] = Callable[P, int]"), indent=4))
Module(
body=[
TypeAlias(
name=Name(id='Alias', ctx=Store()),
type_params=[
ParamSpec(name='P')],
ParamSpec(
name='P',
default_value=Tuple(
elts=[
Name(id='int', ctx=Load()),
Name(id='str', ctx=Load())],
ctx=Load()))],
value=Subscript(
value=Name(id='Callable', ctx=Load()),
slice=Tuple(
@ -1843,24 +1818,30 @@ aliases.
Name(id='P', ctx=Load()),
Name(id='int', ctx=Load())],
ctx=Load()),
ctx=Load()))],
type_ignores=[])
ctx=Load()))])
.. versionadded:: 3.12
.. class:: TypeVarTuple(name)
.. versionchanged:: 3.13
Added the *default_value* parameter.
A :class:`typing.TypeVarTuple`. ``name`` is the name of the type variable tuple.
.. class:: TypeVarTuple(name, default_value)
A :class:`typing.TypeVarTuple`. *name* is the name of the type variable tuple.
*default_value* is the default value; if the :class:`!TypeVarTuple` has no
default, this attribute will be set to ``None``.
.. doctest::
>>> print(ast.dump(ast.parse("type Alias[*Ts] = tuple[*Ts]"), indent=4))
>>> print(ast.dump(ast.parse("type Alias[*Ts = ()] = tuple[*Ts]"), indent=4))
Module(
body=[
TypeAlias(
name=Name(id='Alias', ctx=Store()),
type_params=[
TypeVarTuple(name='Ts')],
TypeVarTuple(
name='Ts',
default_value=Tuple(ctx=Load()))],
value=Subscript(
value=Name(id='tuple', ctx=Load()),
slice=Tuple(
@ -1869,11 +1850,13 @@ aliases.
value=Name(id='Ts', ctx=Load()),
ctx=Load())],
ctx=Load()),
ctx=Load()))],
type_ignores=[])
ctx=Load()))])
.. versionadded:: 3.12
.. versionchanged:: 3.13
Added the *default_value* parameter.
Function and class definitions
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@ -1910,15 +1893,10 @@ Function and class definitions
Expr(
value=Lambda(
args=arguments(
posonlyargs=[],
args=[
arg(arg='x'),
arg(arg='y')],
kwonlyargs=[],
kw_defaults=[],
defaults=[]),
body=Constant(value=Ellipsis)))],
type_ignores=[])
arg(arg='y')]),
body=Constant(value=Ellipsis)))])
.. class:: arguments(posonlyargs, args, vararg, kwonlyargs, kw_defaults, kwarg, defaults)
@ -1957,7 +1935,6 @@ Function and class definitions
FunctionDef(
name='f',
args=arguments(
posonlyargs=[],
args=[
arg(
arg='a',
@ -1980,9 +1957,7 @@ Function and class definitions
decorator_list=[
Name(id='decorator1', ctx=Load()),
Name(id='decorator2', ctx=Load())],
returns=Constant(value='return annotation'),
type_params=[])],
type_ignores=[])
returns=Constant(value='return annotation'))])
.. class:: Return(value)
@ -1995,8 +1970,7 @@ Function and class definitions
Module(
body=[
Return(
value=Constant(value=4))],
type_ignores=[])
value=Constant(value=4))])
.. class:: Yield(value)
@ -2012,16 +1986,14 @@ Function and class definitions
body=[
Expr(
value=Yield(
value=Name(id='x', ctx=Load())))],
type_ignores=[])
value=Name(id='x', ctx=Load())))])
>>> print(ast.dump(ast.parse('yield from x'), indent=4))
Module(
body=[
Expr(
value=YieldFrom(
value=Name(id='x', ctx=Load())))],
type_ignores=[])
value=Name(id='x', ctx=Load())))])
.. class:: Global(names)
@ -2038,8 +2010,7 @@ Function and class definitions
names=[
'x',
'y',
'z'])],
type_ignores=[])
'z'])])
>>> print(ast.dump(ast.parse('nonlocal x,y,z'), indent=4))
Module(
@ -2048,8 +2019,7 @@ Function and class definitions
names=[
'x',
'y',
'z'])],
type_ignores=[])
'z'])])
.. class:: ClassDef(name, bases, keywords, body, decorator_list, type_params)
@ -2089,9 +2059,7 @@ Function and class definitions
Pass()],
decorator_list=[
Name(id='decorator1', ctx=Load()),
Name(id='decorator2', ctx=Load())],
type_params=[])],
type_ignores=[])
Name(id='decorator2', ctx=Load())])])
.. versionchanged:: 3.12
Added ``type_params``.
@ -2123,22 +2091,12 @@ Async and await
body=[
AsyncFunctionDef(
name='f',
args=arguments(
posonlyargs=[],
args=[],
kwonlyargs=[],
kw_defaults=[],
defaults=[]),
args=arguments(),
body=[
Expr(
value=Await(
value=Call(
func=Name(id='other_func', ctx=Load()),
args=[],
keywords=[])))],
decorator_list=[],
type_params=[])],
type_ignores=[])
func=Name(id='other_func', ctx=Load()))))])])
.. class:: AsyncFor(target, iter, body, orelse, type_comment)
@ -2425,7 +2383,7 @@ and classes for traversing abstract syntax trees:
node = YourTransformer().visit(node)
.. function:: dump(node, annotate_fields=True, include_attributes=False, *, indent=None)
.. function:: dump(node, annotate_fields=True, include_attributes=False, *, indent=None, show_empty=False)
Return a formatted dump of the tree in *node*. This is mainly useful for
debugging purposes. If *annotate_fields* is true (by default),
@ -2442,9 +2400,42 @@ and classes for traversing abstract syntax trees:
indents that many spaces per level. If *indent* is a string (such as ``"\t"``),
that string is used to indent each level.
If *show_empty* is ``False`` (the default), empty lists and fields that are ``None``
will be omitted from the output.
.. versionchanged:: 3.9
Added the *indent* option.
.. versionchanged:: 3.13
Added the *show_empty* option.
.. doctest::
>>> print(ast.dump(ast.parse("""\
... async def f():
... await other_func()
... """), indent=4, show_empty=True))
Module(
body=[
AsyncFunctionDef(
name='f',
args=arguments(
posonlyargs=[],
args=[],
kwonlyargs=[],
kw_defaults=[],
defaults=[]),
body=[
Expr(
value=Await(
value=Call(
func=Name(id='other_func', ctx=Load()),
args=[],
keywords=[])))],
decorator_list=[],
type_params=[])],
type_ignores=[])
.. _ast-compiler-flags:
@ -2536,7 +2527,8 @@ to stdout. Otherwise, the content is read from stdin.
code that generated them. This is helpful for tools that make source code
transformations.
`leoAst.py <https://leoeditor.com/appendices.html#leoast-py>`_ unifies the
`leoAst.py <https://leo-editor.github.io/leo-editor/appendices.html#leoast-py>`_
unifies the
token-based and parse-tree-based views of python programs by inserting
two-way links between tokens and ast nodes.
@ -2548,4 +2540,4 @@ to stdout. Otherwise, the content is read from stdin.
`Parso <https://parso.readthedocs.io>`_ is a Python parser that supports
error recovery and round-trip parsing for different Python versions (in
multiple Python versions). Parso is also able to list multiple syntax errors
in your python file.
in your Python file.

View file

@ -796,7 +796,7 @@ Creating network servers
:class:`str`, :class:`bytes`, and :class:`~pathlib.Path` paths
are supported.
If *cleanup_socket* is True then the Unix socket will automatically
If *cleanup_socket* is true then the Unix socket will automatically
be removed from the filesystem when the server is closed, unless the
socket has been replaced after the server has been created.

View file

@ -62,6 +62,9 @@ Queue
Remove and return an item from the queue. If queue is empty,
wait until an item is available.
Raises :exc:`QueueShutDown` if the queue has been shut down and
is empty, or if the queue has been shut down immediately.
.. method:: get_nowait()
Return an item if one is immediately available, else raise
@ -82,6 +85,8 @@ Queue
Put an item into the queue. If the queue is full, wait until a
free slot is available before adding the item.
Raises :exc:`QueueShutDown` if the queue has been shut down.
.. method:: put_nowait(item)
Put an item into the queue without blocking.
@ -92,6 +97,22 @@ Queue
Return the number of items in the queue.
.. method:: shutdown(immediate=False)
Shut down the queue, making :meth:`~Queue.get` and :meth:`~Queue.put`
raise :exc:`QueueShutDown`.
By default, :meth:`~Queue.get` on a shut down queue will only
raise once the queue is empty. Set *immediate* to true to make
:meth:`~Queue.get` raise immediately instead.
All blocked callers of :meth:`~Queue.put` and :meth:`~Queue.get`
will be unblocked. If *immediate* is true, a task will be marked
as done for each remaining item in the queue, which may unblock
callers of :meth:`~Queue.join`.
.. versionadded:: 3.13
.. method:: task_done()
Indicate that a formerly enqueued task is complete.
@ -105,6 +126,9 @@ Queue
call was received for every item that had been :meth:`~Queue.put`
into the queue).
``shutdown(immediate=True)`` calls :meth:`task_done` for each
remaining item in the queue.
Raises :exc:`ValueError` if called more times than there were
items placed in the queue.
@ -145,6 +169,14 @@ Exceptions
on a queue that has reached its *maxsize*.
.. exception:: QueueShutDown
Exception raised when :meth:`~Queue.put` or :meth:`~Queue.get` is
called on a queue which has been shut down.
.. versionadded:: 3.13
Examples
========

View file

@ -260,8 +260,19 @@ StreamReader
buffer is reset. The :attr:`IncompleteReadError.partial` attribute
may contain a portion of the separator.
The *separator* may also be a tuple of separators. In this
case the return value will be the shortest possible that has any
separator as the suffix. For the purposes of :exc:`LimitOverrunError`,
the shortest possible separator is considered to be the one that
matched.
.. versionadded:: 3.5.2
.. versionchanged:: 3.13
The *separator* parameter may now be a :class:`tuple` of
separators.
.. method:: at_eof()
Return ``True`` if the buffer is empty and :meth:`feed_eof`

View file

@ -392,6 +392,27 @@ is also included in the exception group.
The same special case is made for
:exc:`KeyboardInterrupt` and :exc:`SystemExit` as in the previous paragraph.
Task groups are careful not to mix up the internal cancellation used to
"wake up" their :meth:`~object.__aexit__` with cancellation requests
for the task in which they are running made by other parties.
In particular, when one task group is syntactically nested in another,
and both experience an exception in one of their child tasks simultaneously,
the inner task group will process its exceptions, and then the outer task group
will receive another cancellation and process its own exceptions.
In the case where a task group is cancelled externally and also must
raise an :exc:`ExceptionGroup`, it will call the parent task's
:meth:`~asyncio.Task.cancel` method. This ensures that a
:exc:`asyncio.CancelledError` will be raised at the next
:keyword:`await`, so the cancellation is not lost.
Task groups preserve the cancellation count
reported by :meth:`asyncio.Task.cancelling`.
.. versionchanged:: 3.13
Improved handling of simultaneous internal and external cancellations
and correct preservation of cancellation counts.
Sleeping
========
@ -517,7 +538,7 @@ Running Tasks Concurrently
# [2, 6, 24]
.. note::
If *return_exceptions* is False, cancelling gather() after it
If *return_exceptions* is false, cancelling gather() after it
has been marked done won't cancel any submitted awaitables.
For instance, gather can be marked done after propagating an
exception to the caller, therefore, calling ``gather.cancel()``
@ -867,19 +888,50 @@ Waiting Primitives
.. function:: as_completed(aws, *, timeout=None)
Run :ref:`awaitable objects <asyncio-awaitables>` in the *aws*
iterable concurrently. Return an iterator of coroutines.
Each coroutine returned can be awaited to get the earliest next
result from the iterable of the remaining awaitables.
Run :ref:`awaitable objects <asyncio-awaitables>` in the *aws* iterable
concurrently. The returned object can be iterated to obtain the results
of the awaitables as they finish.
Raises :exc:`TimeoutError` if the timeout occurs before
all Futures are done.
The object returned by ``as_completed()`` can be iterated as an
:term:`asynchronous iterator` or a plain :term:`iterator`. When asynchronous
iteration is used, the originally-supplied awaitables are yielded if they
are tasks or futures. This makes it easy to correlate previously-scheduled
tasks with their results. Example::
Example::
ipv4_connect = create_task(open_connection("127.0.0.1", 80))
ipv6_connect = create_task(open_connection("::1", 80))
tasks = [ipv4_connect, ipv6_connect]
for coro in as_completed(aws):
earliest_result = await coro
# ...
async for earliest_connect in as_completed(tasks):
# earliest_connect is done. The result can be obtained by
# awaiting it or calling earliest_connect.result()
reader, writer = await earliest_connect
if earliest_connect is ipv6_connect:
print("IPv6 connection established.")
else:
print("IPv4 connection established.")
During asynchronous iteration, implicitly-created tasks will be yielded for
supplied awaitables that aren't tasks or futures.
When used as a plain iterator, each iteration yields a new coroutine that
returns the result or raises the exception of the next completed awaitable.
This pattern is compatible with Python versions older than 3.13::
ipv4_connect = create_task(open_connection("127.0.0.1", 80))
ipv6_connect = create_task(open_connection("::1", 80))
tasks = [ipv4_connect, ipv6_connect]
for next_connect in as_completed(tasks):
# next_connect is not one of the original task objects. It must be
# awaited to obtain the result value or raise the exception of the
# awaitable that finishes next.
reader, writer = await next_connect
A :exc:`TimeoutError` is raised if the timeout occurs before all awaitables
are done. This is raised by the ``async for`` loop during asynchronous
iteration or by the coroutines yielded during plain iteration.
.. versionchanged:: 3.10
Removed the *loop* parameter.
@ -891,6 +943,10 @@ Waiting Primitives
.. versionchanged:: 3.12
Added support for generators yielding tasks.
.. versionchanged:: 3.13
The result can now be used as either an :term:`asynchronous iterator`
or as a plain :term:`iterator` (previously it was only a plain iterator).
Running in Threads
==================
@ -1330,10 +1386,19 @@ Task Object
with :meth:`uncancel`. :class:`TaskGroup` context managers use
:func:`uncancel` in a similar fashion.
If end-user code is, for some reason, suppresing cancellation by
If end-user code is, for some reason, suppressing cancellation by
catching :exc:`CancelledError`, it needs to call this method to remove
the cancellation state.
When this method decrements the cancellation count to zero,
the method checks if a previous :meth:`cancel` call had arranged
for :exc:`CancelledError` to be thrown into the task.
If it hasn't been thrown yet, that arrangement will be
rescinded (by resetting the internal ``_must_cancel`` flag).
.. versionchanged:: 3.13
Changed to rescind pending cancellation requests upon reaching zero.
.. method:: cancelling()
Return the number of pending cancellation requests to this Task, i.e.,

View file

@ -1,5 +1,5 @@
:mod:`asyncio` --- Asynchronous I/O
===================================
:mod:`!asyncio` --- Asynchronous I/O
====================================
.. module:: asyncio
:synopsis: Asynchronous I/O.

View file

@ -1,5 +1,5 @@
:mod:`atexit` --- Exit handlers
===============================
:mod:`!atexit` --- Exit handlers
================================
.. module:: atexit
:synopsis: Register and execute cleanup functions.

View file

@ -1,5 +1,5 @@
:mod:`base64` --- Base16, Base32, Base64, Base85 Data Encodings
===============================================================
:mod:`!base64` --- Base16, Base32, Base64, Base85 Data Encodings
================================================================
.. module:: base64
:synopsis: RFC 4648: Base16, Base32, Base64 Data Encodings;

View file

@ -1,5 +1,5 @@
:mod:`bdb` --- Debugger framework
=================================
:mod:`!bdb` --- Debugger framework
==================================
.. module:: bdb
:synopsis: Debugger framework.
@ -86,7 +86,7 @@ The :mod:`bdb` module also defines two classes:
.. attribute:: temporary
True if a :class:`Breakpoint` at (file, line) is temporary.
``True`` if a :class:`Breakpoint` at (file, line) is temporary.
.. attribute:: cond
@ -99,7 +99,7 @@ The :mod:`bdb` module also defines two classes:
.. attribute:: enabled
True if :class:`Breakpoint` is enabled.
``True`` if :class:`Breakpoint` is enabled.
.. attribute:: bpbynumber
@ -215,22 +215,22 @@ The :mod:`bdb` module also defines two classes:
.. method:: is_skipped_line(module_name)
Return True if *module_name* matches any skip pattern.
Return ``True`` if *module_name* matches any skip pattern.
.. method:: stop_here(frame)
Return True if *frame* is below the starting frame in the stack.
Return ``True`` if *frame* is below the starting frame in the stack.
.. method:: break_here(frame)
Return True if there is an effective breakpoint for this line.
Return ``True`` if there is an effective breakpoint for this line.
Check whether a line or function breakpoint exists and is in effect. Delete temporary
breakpoints based on information from :func:`effective`.
.. method:: break_anywhere(frame)
Return True if any breakpoint exists for *frame*'s filename.
Return ``True`` if any breakpoint exists for *frame*'s filename.
Derived classes should override these methods to gain control over debugger
operation.
@ -240,6 +240,9 @@ The :mod:`bdb` module also defines two classes:
Called from :meth:`dispatch_call` if a break might stop inside the
called function.
*argument_list* is not used anymore and will always be ``None``.
The argument is kept for backwards compatibility.
.. method:: user_line(frame)
Called from :meth:`dispatch_line` when either :meth:`stop_here` or
@ -286,6 +289,10 @@ The :mod:`bdb` module also defines two classes:
Start debugging from *frame*. If *frame* is not specified, debugging
starts from caller's frame.
.. versionchanged:: 3.13
:func:`set_trace` will enter the debugger immediately, rather than
on the next line of code to be executed.
.. method:: set_continue()
Stop only at breakpoints or when finished. If there are no breakpoints,
@ -341,7 +348,7 @@ The :mod:`bdb` module also defines two classes:
.. method:: get_break(filename, lineno)
Return True if there is a breakpoint for *lineno* in *filename*.
Return ``True`` if there is a breakpoint for *lineno* in *filename*.
.. method:: get_breaks(filename, lineno)
@ -405,7 +412,7 @@ Finally, the module defines the following functions:
.. function:: checkfuncname(b, frame)
Return True if we should break here, depending on the way the
Return ``True`` if we should break here, depending on the way the
:class:`Breakpoint` *b* was set.
If it was set via line number, it checks if
@ -424,14 +431,14 @@ Finally, the module defines the following functions:
:attr:`bplist <bdb.Breakpoint.bplist>` for the
(:attr:`file <bdb.Breakpoint.file>`, :attr:`line <bdb.Breakpoint.line>`)
(which must exist) that is :attr:`enabled <bdb.Breakpoint.enabled>`, for
which :func:`checkfuncname` is True, and that has neither a False
which :func:`checkfuncname` is true, and that has neither a false
:attr:`condition <bdb.Breakpoint.cond>` nor positive
:attr:`ignore <bdb.Breakpoint.ignore>` count. The *flag*, meaning that a
temporary breakpoint should be deleted, is False only when the
temporary breakpoint should be deleted, is ``False`` only when the
:attr:`cond <bdb.Breakpoint.cond>` cannot be evaluated (in which case,
:attr:`ignore <bdb.Breakpoint.ignore>` count is ignored).
If no such entry exists, then (None, None) is returned.
If no such entry exists, then ``(None, None)`` is returned.
.. function:: set_trace()

View file

@ -1,5 +1,5 @@
:mod:`binascii` --- Convert between binary and ASCII
====================================================
:mod:`!binascii` --- Convert between binary and ASCII
=====================================================
.. module:: binascii
:synopsis: Tools for converting between binary and various ASCII-encoded binary

View file

@ -1,5 +1,5 @@
:mod:`bisect` --- Array bisection algorithm
===========================================
:mod:`!bisect` --- Array bisection algorithm
============================================
.. module:: bisect
:synopsis: Array bisection algorithms for binary searching.

View file

@ -1,5 +1,5 @@
:mod:`builtins` --- Built-in objects
====================================
:mod:`!builtins` --- Built-in objects
=====================================
.. module:: builtins
:synopsis: The module that provides the built-in namespace.

View file

@ -1,5 +1,5 @@
:mod:`bz2` --- Support for :program:`bzip2` compression
=======================================================
:mod:`!bz2` --- Support for :program:`bzip2` compression
========================================================
.. module:: bz2
:synopsis: Interfaces for bzip2 compression and decompression.
@ -91,7 +91,7 @@ The :mod:`bz2` module contains:
and :meth:`~io.IOBase.truncate`.
Iteration and the :keyword:`with` statement are supported.
:class:`BZ2File` also provides the following methods:
:class:`BZ2File` also provides the following methods and attributes:
.. method:: peek([n])
@ -148,6 +148,19 @@ The :mod:`bz2` module contains:
.. versionadded:: 3.3
.. attribute:: mode
``'rb'`` for reading and ``'wb'`` for writing.
.. versionadded:: 3.13
.. attribute:: name
The bzip2 file name. Equivalent to the :attr:`~io.FileIO.name`
attribute of the underlying :term:`file object`.
.. versionadded:: 3.13
.. versionchanged:: 3.1
Support for the :keyword:`with` statement was added.

View file

@ -1,5 +1,5 @@
:mod:`calendar` --- General calendar-related functions
======================================================
:mod:`!calendar` --- General calendar-related functions
=======================================================
.. module:: calendar
:synopsis: Functions for working with calendars, including some emulation

View file

@ -1,5 +1,5 @@
:mod:`cmath` --- Mathematical functions for complex numbers
===========================================================
:mod:`!cmath` --- Mathematical functions for complex numbers
============================================================
.. module:: cmath
:synopsis: Mathematical functions for complex numbers.

View file

@ -1,5 +1,5 @@
:mod:`cmd` --- Support for line-oriented command interpreters
=============================================================
:mod:`!cmd` --- Support for line-oriented command interpreters
==============================================================
.. module:: cmd
:synopsis: Build line-oriented command interpreters.

View file

@ -36,6 +36,7 @@ The following modules have a command-line interface.
* :mod:`pyclbr`
* :mod:`pydoc`
* :mod:`quopri`
* :ref:`random <random-cli>`
* :mod:`runpy`
* :ref:`site <site-commandline>`
* :ref:`sqlite3 <sqlite3-cli>`

View file

@ -1,5 +1,5 @@
:mod:`code` --- Interpreter base classes
========================================
:mod:`!code` --- Interpreter base classes
=========================================
.. module:: code
:synopsis: Facilities to implement read-eval-print loops.
@ -27,7 +27,7 @@ build applications which provide an interactive interpreter prompt.
Closely emulate the behavior of the interactive Python interpreter. This class
builds on :class:`InteractiveInterpreter` and adds prompting using the familiar
``sys.ps1`` and ``sys.ps2``, and input buffering. If *local_exit* is True,
``sys.ps1`` and ``sys.ps2``, and input buffering. If *local_exit* is true,
``exit()`` and ``quit()`` in the console will not raise :exc:`SystemExit`, but
instead return to the calling code.
@ -41,7 +41,7 @@ build applications which provide an interactive interpreter prompt.
the :meth:`InteractiveConsole.raw_input` method, if provided. If *local* is
provided, it is passed to the :class:`InteractiveConsole` constructor for
use as the default namespace for the interpreter loop. If *local_exit* is provided,
it is passed to the :class:`InteractiveConsole` constructor. The :meth:`interact`
it is passed to the :class:`InteractiveConsole` constructor. The :meth:`~InteractiveConsole.interact`
method of the instance is then run with *banner* and *exitmsg* passed as the
banner and exit message to use, if provided. The console object is discarded
after use.

View file

@ -1,5 +1,5 @@
:mod:`codecs` --- Codec registry and base classes
=================================================
:mod:`!codecs` --- Codec registry and base classes
==================================================
.. module:: codecs
:synopsis: Encode and decode data and streams.
@ -1478,7 +1478,7 @@ Internationalized Domain Names (IDN)). It builds upon the ``punycode`` encoding
and :mod:`stringprep`.
If you need the IDNA 2008 standard from :rfc:`5891` and :rfc:`5895`, use the
third-party `idna module <https://pypi.org/project/idna/>`_.
third-party :pypi:`idna` module.
These RFCs together define a protocol to support non-ASCII characters in domain
names. A domain name containing non-ASCII characters (such as

View file

@ -1,5 +1,5 @@
:mod:`codeop` --- Compile Python code
=====================================
:mod:`!codeop` --- Compile Python code
======================================
.. module:: codeop
:synopsis: Compile (possibly incomplete) Python code.

View file

@ -1,5 +1,5 @@
:mod:`collections.abc` --- Abstract Base Classes for Containers
===============================================================
:mod:`!collections.abc` --- Abstract Base Classes for Containers
================================================================
.. module:: collections.abc
:synopsis: Abstract base classes for containers
@ -141,9 +141,6 @@ ABC Inherits from Abstract Methods Mi
``__len__``,
``insert``
:class:`ByteString` :class:`Sequence` ``__getitem__``, Inherited :class:`Sequence` methods
``__len__``
:class:`Set` :class:`Collection` ``__contains__``, ``__le__``, ``__lt__``, ``__eq__``, ``__ne__``,
``__iter__``, ``__gt__``, ``__ge__``, ``__and__``, ``__or__``,
``__len__`` ``__sub__``, ``__xor__``, and ``isdisjoint``
@ -257,7 +254,6 @@ Collections Abstract Base Classes -- Detailed Descriptions
.. class:: Sequence
MutableSequence
ByteString
ABCs for read-only and mutable :term:`sequences <sequence>`.
@ -274,12 +270,6 @@ Collections Abstract Base Classes -- Detailed Descriptions
The index() method added support for *stop* and *start*
arguments.
.. deprecated-removed:: 3.12 3.14
The :class:`ByteString` ABC has been deprecated.
For use in typing, prefer a union, like ``bytes | bytearray``, or
:class:`collections.abc.Buffer`.
For use as an ABC, prefer :class:`Sequence` or :class:`collections.abc.Buffer`.
.. class:: Set
MutableSet

View file

@ -1,5 +1,5 @@
:mod:`collections` --- Container datatypes
==========================================
:mod:`!collections` --- Container datatypes
===========================================
.. module:: collections
:synopsis: Container datatypes
@ -134,7 +134,7 @@ The class can be used to simulate nested scopes and is useful in templating.
:attr:`~collections.ChainMap.parents` property.
* The `Nested Contexts recipe
<https://code.activestate.com/recipes/577434/>`_ has options to control
<https://code.activestate.com/recipes/577434-nested-contexts-a-chain-of-mapping-objects/>`_ has options to control
whether writes and other mutations apply only to the first mapping or to
any mapping in the chain.

View file

@ -1,5 +1,5 @@
:mod:`colorsys` --- Conversions between color systems
=====================================================
:mod:`!colorsys` --- Conversions between color systems
======================================================
.. module:: colorsys
:synopsis: Conversion functions between RGB and other color systems.

View file

@ -1,5 +1,5 @@
:mod:`compileall` --- Byte-compile Python libraries
===================================================
:mod:`!compileall` --- Byte-compile Python libraries
====================================================
.. module:: compileall
:synopsis: Tools for byte-compiling all Python source files in a directory tree.
@ -226,7 +226,7 @@ Public functions
The *invalidation_mode* parameter was added.
.. versionchanged:: 3.7.2
The *invalidation_mode* parameter's default value is updated to None.
The *invalidation_mode* parameter's default value is updated to ``None``.
.. versionchanged:: 3.8
Setting *workers* to 0 now chooses the optimal number of cores.
@ -289,7 +289,7 @@ Public functions
The *invalidation_mode* parameter was added.
.. versionchanged:: 3.7.2
The *invalidation_mode* parameter's default value is updated to None.
The *invalidation_mode* parameter's default value is updated to ``None``.
.. versionchanged:: 3.9
Added *stripdir*, *prependdir*, *limit_sl_dest* and *hardlink_dupes* arguments.
@ -318,7 +318,7 @@ Public functions
The *invalidation_mode* parameter was added.
.. versionchanged:: 3.7.2
The *invalidation_mode* parameter's default value is updated to None.
The *invalidation_mode* parameter's default value is updated to ``None``.
To force a recompile of all the :file:`.py` files in the :file:`Lib/`
subdirectory and all its subdirectories::

View file

@ -1,5 +1,5 @@
:mod:`concurrent.futures` --- Launching parallel tasks
======================================================
:mod:`!concurrent.futures` --- Launching parallel tasks
=======================================================
.. module:: concurrent.futures
:synopsis: Execute computations concurrently using threads or processes.

View file

@ -1,5 +1,5 @@
:mod:`configparser` --- Configuration file parser
=================================================
:mod:`!configparser` --- Configuration file parser
==================================================
.. module:: configparser
:synopsis: Configuration file parser.
@ -274,6 +274,11 @@ may be treated as parts of multiline values or ignored.
By default, a valid section name can be any string that does not contain '\\n'.
To change this, see :attr:`ConfigParser.SECTCRE`.
The first section name may be omitted if the parser is configured to allow an
unnamed top level section with ``allow_unnamed_section=True``. In this case,
the keys/values may be retrieved by :const:`UNNAMED_SECTION` as in
``config[UNNAMED_SECTION]``.
Configuration files may include comments, prefixed by specific
characters (``#`` and ``;`` by default [1]_). Comments may appear on
their own on an otherwise empty line, possibly indented. [1]_
@ -325,6 +330,27 @@ For example:
# Did I mention we can indent comments, too?
.. _unnamed-sections:
Unnamed Sections
----------------
The name of the first section (or unique) may be omitted and values
retrieved by the :const:`UNNAMED_SECTION` attribute.
.. doctest::
>>> config = """
... option = value
...
... [ Section 2 ]
... another = val
... """
>>> unnamed = configparser.ConfigParser(allow_unnamed_section=True)
>>> unnamed.read_string(config)
>>> unnamed.get(configparser.UNNAMED_SECTION, 'option')
'value'
Interpolation of values
-----------------------
@ -1216,6 +1242,11 @@ ConfigParser Objects
names is stripped before :meth:`optionxform` is called.
.. data:: UNNAMED_SECTION
A special object representing a section name used to reference the unnamed section (see :ref:`unnamed-sections`).
.. data:: MAX_INTERPOLATION_DEPTH
The maximum depth for recursive interpolation for :meth:`~configparser.ConfigParser.get` when the *raw*

View file

@ -53,9 +53,12 @@ A small number of constants live in the built-in namespace. They are:
See :exc:`NotImplementedError` for details on when to use it.
.. versionchanged:: 3.9
Evaluating :data:`!NotImplemented` in a boolean context is deprecated. While
it currently evaluates as true, it will emit a :exc:`DeprecationWarning`.
It will raise a :exc:`TypeError` in a future version of Python.
Evaluating :data:`!NotImplemented` in a boolean context was deprecated.
.. versionchanged:: 3.14
Evaluating :data:`!NotImplemented` in a boolean context now raises a :exc:`TypeError`.
It previously evaluated to :const:`True` and emitted a :exc:`DeprecationWarning`
since Python 3.9.
.. index:: single: ...; ellipsis literal

View file

@ -1,5 +1,5 @@
:mod:`contextvars` --- Context Variables
========================================
:mod:`!contextvars` --- Context Variables
=========================================
.. module:: contextvars
:synopsis: Context Variables

View file

@ -1,5 +1,5 @@
:mod:`copy` --- Shallow and deep copy operations
================================================
:mod:`!copy` --- Shallow and deep copy operations
=================================================
.. module:: copy
:synopsis: Shallow and deep copy operations.

View file

@ -1,5 +1,5 @@
:mod:`copyreg` --- Register :mod:`pickle` support functions
===========================================================
:mod:`!copyreg` --- Register :mod:`!pickle` support functions
=============================================================
.. module:: copyreg
:synopsis: Register pickle support functions.

View file

@ -1,5 +1,5 @@
:mod:`csv` --- CSV File Reading and Writing
===========================================
:mod:`!csv` --- CSV File Reading and Writing
============================================
.. module:: csv
:synopsis: Write and read tabular data to and from delimited files.
@ -156,8 +156,10 @@ The :mod:`csv` module defines the following classes:
The *fieldnames* parameter is a :term:`sequence`. If *fieldnames* is
omitted, the values in the first row of file *f* will be used as the
fieldnames. Regardless of how the fieldnames are determined, the
dictionary preserves their original ordering.
fieldnames and will be omitted from the results. If
*fieldnames* is provided, they will be used and the first row will be
included in the results. Regardless of how the fieldnames are determined,
the dictionary preserves their original ordering.
If a row has more fields than fieldnames, the remaining data is put in a
list and stored with the fieldname specified by *restkey* (which defaults
@ -347,8 +349,8 @@ The :mod:`csv` module defines the following constants:
``None``. This is similar to :data:`QUOTE_ALL`, except that if a
field value is ``None`` an empty (unquoted) string is written.
Instructs :class:`reader` objects to interpret an empty (unquoted) field as None and
to otherwise behave as :data:`QUOTE_ALL`.
Instructs :class:`reader` objects to interpret an empty (unquoted) field
as ``None`` and to otherwise behave as :data:`QUOTE_ALL`.
.. versionadded:: 3.12

View file

@ -1,5 +1,5 @@
:mod:`ctypes` --- A foreign function library for Python
=======================================================
:mod:`!ctypes` --- A foreign function library for Python
========================================================
.. module:: ctypes
:synopsis: A foreign function library for Python.
@ -2077,13 +2077,13 @@ Utility functions
Does the same as the C ``sizeof`` operator.
.. function:: string_at(address, size=-1)
.. function:: string_at(ptr, size=-1)
This function returns the C string starting at memory address *address* as a bytes
object. If size is specified, it is used as size, otherwise the string is assumed
Return the byte string at *void \*ptr*.
If *size* is specified, it is used as size, otherwise the string is assumed
to be zero-terminated.
.. audit-event:: ctypes.string_at address,size ctypes.string_at
.. audit-event:: ctypes.string_at ptr,size ctypes.string_at
.. function:: WinError(code=None, descr=None)
@ -2099,14 +2099,14 @@ Utility functions
alias of :exc:`OSError`.
.. function:: wstring_at(address, size=-1)
.. function:: wstring_at(ptr, size=-1)
This function returns the wide character string starting at memory address
*address* as a string. If *size* is specified, it is used as the number of
Return the wide-character string at *void \*ptr*.
If *size* is specified, it is used as the number of
characters of the string, otherwise the string is assumed to be
zero-terminated.
.. audit-event:: ctypes.wstring_at address,size ctypes.wstring_at
.. audit-event:: ctypes.wstring_at ptr,size ctypes.wstring_at
.. _ctypes-data-types:

Some files were not shown because too many files have changed in this diff Show more