mirror of
https://github.com/python/cpython.git
synced 2025-12-23 09:19:18 +00:00
Merge branch 'main' into manjusaka/gh107398
This commit is contained in:
commit
de42818008
2166 changed files with 223203 additions and 95148 deletions
|
|
@ -1,4 +1,4 @@
|
|||
trigger: ['main', '3.13', '3.12', '3.11', '3.10', '3.9', '3.8']
|
||||
trigger: ['main', '3.*']
|
||||
|
||||
jobs:
|
||||
- job: Prebuild
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
root = true
|
||||
|
||||
[*.{py,c,cpp,h,js,rst,md,yml}]
|
||||
[*.{py,c,cpp,h,js,rst,md,yml,yaml}]
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
|
|
@ -11,5 +11,5 @@ indent_size = 4
|
|||
[*.rst]
|
||||
indent_size = 3
|
||||
|
||||
[*.{js,yml}]
|
||||
[*.{js,yml,yaml}]
|
||||
indent_size = 2
|
||||
|
|
|
|||
2
.gitattributes
vendored
2
.gitattributes
vendored
|
|
@ -10,6 +10,7 @@
|
|||
*.ico binary
|
||||
*.jpg binary
|
||||
*.pck binary
|
||||
*.pdf binary
|
||||
*.png binary
|
||||
*.psd binary
|
||||
*.tar binary
|
||||
|
|
@ -67,6 +68,7 @@ PCbuild/readme.txt dos
|
|||
**/clinic/*.cpp.h generated
|
||||
**/clinic/*.h.h generated
|
||||
*_db.h generated
|
||||
Doc/c-api/lifecycle.dot.svg generated
|
||||
Doc/data/stable_abi.dat generated
|
||||
Doc/library/token-list.inc generated
|
||||
Include/internal/pycore_ast.h generated
|
||||
|
|
|
|||
60
.github/CODEOWNERS
vendored
60
.github/CODEOWNERS
vendored
|
|
@ -5,11 +5,11 @@
|
|||
# https://git-scm.com/docs/gitignore#_pattern_format
|
||||
|
||||
# GitHub
|
||||
.github/** @ezio-melotti @hugovk
|
||||
.github/** @ezio-melotti @hugovk @AA-Turner
|
||||
|
||||
# pre-commit
|
||||
.pre-commit-config.yaml @hugovk @AlexWaygood
|
||||
.ruff.toml @hugovk @AlexWaygood
|
||||
.ruff.toml @hugovk @AlexWaygood @AA-Turner
|
||||
|
||||
# Build system
|
||||
configure* @erlend-aasland @corona10
|
||||
|
|
@ -30,6 +30,7 @@ Modules/Setup* @erlend-aasland
|
|||
Objects/set* @rhettinger
|
||||
Objects/dict* @methane @markshannon
|
||||
Objects/typevarobject.c @JelleZijlstra
|
||||
Objects/unionobject.c @JelleZijlstra
|
||||
Objects/type* @markshannon
|
||||
Objects/codeobject.c @markshannon
|
||||
Objects/frameobject.c @markshannon
|
||||
|
|
@ -56,6 +57,14 @@ Tools/c-analyzer/ @ericsnowcurrently
|
|||
# dbm
|
||||
**/*dbm* @corona10 @erlend-aasland @serhiy-storchaka
|
||||
|
||||
# Doc/ tools
|
||||
Doc/conf.py @AA-Turner @hugovk
|
||||
Doc/Makefile @AA-Turner @hugovk
|
||||
Doc/make.bat @AA-Turner @hugovk
|
||||
Doc/requirements.txt @AA-Turner @hugovk
|
||||
Doc/_static/** @AA-Turner @hugovk
|
||||
Doc/tools/** @AA-Turner @hugovk
|
||||
|
||||
# runtime state/lifecycle
|
||||
**/*pylifecycle* @ericsnowcurrently
|
||||
**/*pystate* @ericsnowcurrently
|
||||
|
|
@ -96,13 +105,18 @@ Doc/library/site.rst @FFY00
|
|||
Lib/test/test_except*.py @iritkatriel
|
||||
Objects/exceptions.c @iritkatriel
|
||||
|
||||
# Hashing
|
||||
**/*hashlib* @gpshead @tiran
|
||||
**/*pyhash* @gpshead @tiran
|
||||
**/sha* @gpshead @tiran
|
||||
Modules/md5* @gpshead @tiran
|
||||
**/*blake* @gpshead @tiran
|
||||
Modules/_hacl/** @gpshead
|
||||
# Hashing & cryptographic primitives
|
||||
**/*hashlib* @gpshead @tiran @picnixz
|
||||
**/*hashopenssl* @gpshead @tiran @picnixz
|
||||
**/*pyhash* @gpshead @tiran @picnixz
|
||||
Modules/*blake* @gpshead @tiran @picnixz
|
||||
Modules/*md5* @gpshead @tiran @picnixz
|
||||
Modules/*sha* @gpshead @tiran @picnixz
|
||||
Modules/_hacl/** @gpshead @picnixz
|
||||
**/*hmac* @gpshead @picnixz
|
||||
|
||||
# libssl
|
||||
**/*ssl* @gpshead @picnixz
|
||||
|
||||
# logging
|
||||
**/*logging* @vsajip
|
||||
|
|
@ -154,6 +168,9 @@ Include/internal/pycore_time.h @pganssle @abalkin
|
|||
**/*imap* @python/email-team
|
||||
**/*poplib* @python/email-team
|
||||
|
||||
# Exclude .mailmap from being owned by @python/email-team
|
||||
/.mailmap
|
||||
|
||||
# Garbage collector
|
||||
/Modules/gcmodule.c @pablogsal
|
||||
/Doc/library/gc.rst @pablogsal
|
||||
|
|
@ -171,10 +188,11 @@ Include/internal/pycore_time.h @pganssle @abalkin
|
|||
|
||||
# AST
|
||||
Python/ast.c @isidentical @JelleZijlstra @eclips4
|
||||
Python/ast_opt.c @isidentical @eclips4
|
||||
Python/ast_preprocess.c @isidentical @eclips4
|
||||
Parser/asdl.py @isidentical @JelleZijlstra @eclips4
|
||||
Parser/asdl_c.py @isidentical @JelleZijlstra @eclips4
|
||||
Lib/ast.py @isidentical @JelleZijlstra @eclips4
|
||||
Lib/_ast_unparse.py @isidentical @JelleZijlstra @eclips4
|
||||
Lib/test/test_ast/ @eclips4
|
||||
|
||||
# Mock
|
||||
|
|
@ -280,7 +298,12 @@ Lib/test/test_interpreters/ @ericsnowcurrently
|
|||
**/*-ios* @freakboy3742
|
||||
|
||||
# WebAssembly
|
||||
/Tools/wasm/ @brettcannon @freakboy3742
|
||||
Tools/wasm/config.site-wasm32-emscripten @freakboy3742
|
||||
/Tools/wasm/README.md @brettcannon @freakboy3742
|
||||
/Tools/wasm/wasi-env @brettcannon
|
||||
/Tools/wasm/wasi.py @brettcannon
|
||||
/Tools/wasm/emscripten @freakboy3742
|
||||
/Tools/wasm/wasi @brettcannon
|
||||
|
||||
# SBOM
|
||||
/Misc/externals.spdx.json @sethmlarson
|
||||
|
|
@ -292,6 +315,19 @@ Lib/configparser.py @jaraco
|
|||
Lib/test/test_configparser.py @jaraco
|
||||
|
||||
# Doc sections
|
||||
Doc/reference/ @willingc
|
||||
Doc/reference/ @willingc @AA-Turner
|
||||
|
||||
**/*weakref* @kumaraditya303
|
||||
|
||||
# Colorize
|
||||
Lib/_colorize.py @hugovk
|
||||
Lib/test/test__colorize.py @hugovk
|
||||
|
||||
# Fuzzing
|
||||
Modules/_xxtestfuzz/ @ammaraskar
|
||||
|
||||
# t-strings
|
||||
**/*interpolationobject* @lysnikolaou
|
||||
**/*templateobject* @lysnikolaou
|
||||
**/*templatelib* @lysnikolaou
|
||||
**/*tstring* @lysnikolaou
|
||||
|
|
|
|||
1
.github/ISSUE_TEMPLATE/bug.yml
vendored
1
.github/ISSUE_TEMPLATE/bug.yml
vendored
|
|
@ -40,6 +40,7 @@ body:
|
|||
- "3.12"
|
||||
- "3.13"
|
||||
- "3.14"
|
||||
- "3.15"
|
||||
- "CPython main branch"
|
||||
validations:
|
||||
required: true
|
||||
|
|
|
|||
1
.github/ISSUE_TEMPLATE/crash.yml
vendored
1
.github/ISSUE_TEMPLATE/crash.yml
vendored
|
|
@ -33,6 +33,7 @@ body:
|
|||
- "3.12"
|
||||
- "3.13"
|
||||
- "3.14"
|
||||
- "3.15"
|
||||
- "CPython main branch"
|
||||
validations:
|
||||
required: true
|
||||
|
|
|
|||
10
.github/PULL_REQUEST_TEMPLATE.md
vendored
10
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
|
@ -7,10 +7,10 @@ Please read this comment in its entirety. It's quite important.
|
|||
It should be in the following format:
|
||||
|
||||
```
|
||||
gh-NNNNN: Summary of the changes made
|
||||
gh-NNNNNN: Summary of the changes made
|
||||
```
|
||||
|
||||
Where: gh-NNNNN refers to the GitHub issue number.
|
||||
Where: gh-NNNNNN refers to the GitHub issue number.
|
||||
|
||||
Most PRs will require an issue number. Trivial changes, like fixing a typo, do not need an issue.
|
||||
|
||||
|
|
@ -20,11 +20,11 @@ If this is a backport PR (PR made against branches other than `main`),
|
|||
please ensure that the PR title is in the following format:
|
||||
|
||||
```
|
||||
[X.Y] <title from the original PR> (GH-NNNN)
|
||||
[X.Y] <title from the original PR> (GH-NNNNNN)
|
||||
```
|
||||
|
||||
Where: [X.Y] is the branch name, e.g. [3.6].
|
||||
Where: [X.Y] is the branch name, for example: [3.13].
|
||||
|
||||
GH-NNNN refers to the PR number from `main`.
|
||||
GH-NNNNNN refers to the PR number from `main`.
|
||||
|
||||
-->
|
||||
|
|
|
|||
5
.github/actionlint.yaml
vendored
5
.github/actionlint.yaml
vendored
|
|
@ -1,5 +1,6 @@
|
|||
self-hosted-runner:
|
||||
labels: ["ubuntu-24.04-aarch64", "windows-aarch64"]
|
||||
# Pending https://github.com/rhysd/actionlint/issues/533
|
||||
labels: ["windows-11-arm"]
|
||||
|
||||
config-variables: null
|
||||
|
||||
|
|
@ -7,4 +8,4 @@ paths:
|
|||
.github/workflows/**/*.yml:
|
||||
ignore:
|
||||
- 1st argument of function call is not assignable
|
||||
- SC2(015|038|086|091|097|098|129|155)
|
||||
- SC2(015|038|086|091|097|098|129|155)
|
||||
|
|
|
|||
1
.github/workflows/add-issue-header.yml
vendored
1
.github/workflows/add-issue-header.yml
vendored
|
|
@ -18,6 +18,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: actions/github-script@v7
|
||||
with:
|
||||
|
|
|
|||
340
.github/workflows/build.yml
vendored
340
.github/workflows/build.yml
vendored
|
|
@ -15,41 +15,50 @@ permissions:
|
|||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}-reusable
|
||||
# https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#concurrency
|
||||
# 'group' must be a key uniquely representing a PR or push event.
|
||||
# github.workflow is the workflow name
|
||||
# github.actor is the user invoking the workflow
|
||||
# github.head_ref is the source branch of the PR or otherwise blank
|
||||
# github.run_id is a unique number for the current run
|
||||
group: ${{ github.workflow }}-${{ github.actor }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
FORCE_COLOR: 1
|
||||
|
||||
jobs:
|
||||
check_source:
|
||||
build-context:
|
||||
name: Change detection
|
||||
# To use boolean outputs from this job, parse them as JSON.
|
||||
# Here's some examples:
|
||||
#
|
||||
# if: fromJSON(needs.check_source.outputs.run-docs)
|
||||
# if: fromJSON(needs.build-context.outputs.run-docs)
|
||||
#
|
||||
# ${{
|
||||
# fromJSON(needs.check_source.outputs.run_tests)
|
||||
# fromJSON(needs.build-context.outputs.run-tests)
|
||||
# && 'truthy-branch'
|
||||
# || 'falsy-branch'
|
||||
# }}
|
||||
#
|
||||
uses: ./.github/workflows/reusable-change-detection.yml
|
||||
uses: ./.github/workflows/reusable-context.yml
|
||||
|
||||
check-docs:
|
||||
name: Docs
|
||||
needs: check_source
|
||||
if: fromJSON(needs.check_source.outputs.run-docs)
|
||||
needs: build-context
|
||||
if: fromJSON(needs.build-context.outputs.run-docs)
|
||||
uses: ./.github/workflows/reusable-docs.yml
|
||||
|
||||
check_autoconf_regen:
|
||||
check-autoconf-regen:
|
||||
name: 'Check if Autoconf files are up to date'
|
||||
# Don't use ubuntu-latest but a specific version to make the job
|
||||
# reproducible: to get the same tools versions (autoconf, aclocal, ...)
|
||||
runs-on: ubuntu-24.04
|
||||
container:
|
||||
image: ghcr.io/python/autoconf:2024.11.11.11786316759
|
||||
image: ghcr.io/python/autoconf:2025.01.02.12581854023
|
||||
timeout-minutes: 60
|
||||
needs: check_source
|
||||
if: needs.check_source.outputs.run_tests == 'true'
|
||||
needs: build-context
|
||||
if: needs.build-context.outputs.run-tests == 'true'
|
||||
steps:
|
||||
- name: Install Git
|
||||
run: |
|
||||
|
|
@ -58,11 +67,10 @@ jobs:
|
|||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 1
|
||||
- name: Runner image version
|
||||
run: echo "IMAGE_VERSION=${ImageVersion}" >> "$GITHUB_ENV"
|
||||
persist-credentials: false
|
||||
- name: Check Autoconf and aclocal versions
|
||||
run: |
|
||||
grep "Generated by GNU Autoconf 2.71" configure
|
||||
grep "Generated by GNU Autoconf 2.72" configure
|
||||
grep "aclocal 1.16.5" aclocal.m4
|
||||
grep -q "runstatedir" configure
|
||||
grep -q "PKG_PROG_PKG_CONFIG" aclocal.m4
|
||||
|
|
@ -84,28 +92,30 @@ jobs:
|
|||
exit 1
|
||||
fi
|
||||
|
||||
check_generated_files:
|
||||
check-generated-files:
|
||||
name: 'Check if generated files are up to date'
|
||||
# Don't use ubuntu-latest but a specific version to make the job
|
||||
# reproducible: to get the same tools versions (autoconf, aclocal, ...)
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 60
|
||||
needs: check_source
|
||||
if: needs.check_source.outputs.run_tests == 'true'
|
||||
needs: build-context
|
||||
if: needs.build-context.outputs.run-tests == 'true'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: Runner image version
|
||||
run: echo "IMAGE_VERSION=${ImageVersion}" >> "$GITHUB_ENV"
|
||||
run: echo "IMAGE_OS_VERSION=${ImageOS}-${ImageVersion}" >> "$GITHUB_ENV"
|
||||
- name: Restore config.cache
|
||||
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
|
||||
key: ${{ github.job }}-${{ env.IMAGE_OS_VERSION }}-${{ needs.build-context.outputs.config-hash }}-${{ env.pythonLocation }}
|
||||
- name: Install dependencies
|
||||
run: sudo ./.github/workflows/posix-deps-apt.sh
|
||||
- name: Add ccache to PATH
|
||||
run: echo "PATH=/usr/lib/ccache:$PATH" >> "$GITHUB_ENV"
|
||||
|
|
@ -120,7 +130,7 @@ jobs:
|
|||
- name: Build CPython
|
||||
run: |
|
||||
make -j4 regen-all
|
||||
make regen-stdlib-module-names regen-sbom
|
||||
make regen-stdlib-module-names regen-sbom regen-unicodedata
|
||||
- name: Check for changes
|
||||
run: |
|
||||
git add -u
|
||||
|
|
@ -143,44 +153,37 @@ jobs:
|
|||
if: github.event_name == 'pull_request' # $GITHUB_EVENT_NAME
|
||||
run: make check-c-globals
|
||||
|
||||
build_windows:
|
||||
build-windows:
|
||||
name: >-
|
||||
Windows
|
||||
${{ fromJSON(matrix.free-threading) && '(free-threading)' || '' }}
|
||||
needs: check_source
|
||||
if: fromJSON(needs.check_source.outputs.run_tests)
|
||||
needs: build-context
|
||||
if: fromJSON(needs.build-context.outputs.run-windows-tests)
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os:
|
||||
- windows-latest
|
||||
arch:
|
||||
- x64
|
||||
- Win32
|
||||
- arm64
|
||||
free-threading:
|
||||
- false
|
||||
- true
|
||||
include:
|
||||
- os: windows-latest # FIXME(diegorusso): change to os: windows-aarch64
|
||||
arch: arm64
|
||||
free-threading: false
|
||||
- os: windows-latest # FIXME(diegorusso): change to os: windows-aarch64
|
||||
arch: arm64
|
||||
free-threading: true
|
||||
- os: windows-latest
|
||||
arch: Win32
|
||||
free-threading: false
|
||||
exclude:
|
||||
# Skip Win32 on free-threaded builds
|
||||
- { arch: Win32, free-threading: true }
|
||||
uses: ./.github/workflows/reusable-windows.yml
|
||||
with:
|
||||
os: ${{ matrix.os }}
|
||||
arch: ${{ matrix.arch }}
|
||||
free-threading: ${{ matrix.free-threading }}
|
||||
|
||||
build_windows_msi:
|
||||
build-windows-msi:
|
||||
name: >- # ${{ '' } is a hack to nest jobs under the same sidebar category
|
||||
Windows MSI${{ '' }}
|
||||
needs: check_source
|
||||
if: fromJSON(needs.check_source.outputs.run-win-msi)
|
||||
needs: build-context
|
||||
if: fromJSON(needs.build-context.outputs.run-windows-msi)
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
arch:
|
||||
- x86
|
||||
|
|
@ -190,12 +193,12 @@ jobs:
|
|||
with:
|
||||
arch: ${{ matrix.arch }}
|
||||
|
||||
build_macos:
|
||||
build-macos:
|
||||
name: >-
|
||||
macOS
|
||||
${{ fromJSON(matrix.free-threading) && '(free-threading)' || '' }}
|
||||
needs: check_source
|
||||
if: needs.check_source.outputs.run_tests == 'true'
|
||||
needs: build-context
|
||||
if: needs.build-context.outputs.run-tests == 'true'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
|
|
@ -220,37 +223,55 @@ jobs:
|
|||
free-threading: true
|
||||
uses: ./.github/workflows/reusable-macos.yml
|
||||
with:
|
||||
config_hash: ${{ needs.check_source.outputs.config_hash }}
|
||||
config_hash: ${{ needs.build-context.outputs.config-hash }}
|
||||
free-threading: ${{ matrix.free-threading }}
|
||||
os: ${{ matrix.os }}
|
||||
|
||||
build_ubuntu:
|
||||
build-ubuntu:
|
||||
name: >-
|
||||
Ubuntu
|
||||
${{ fromJSON(matrix.free-threading) && '(free-threading)' || '' }}
|
||||
needs: check_source
|
||||
if: needs.check_source.outputs.run_tests == 'true'
|
||||
${{ fromJSON(matrix.bolt) && '(bolt)' || '' }}
|
||||
needs: build-context
|
||||
if: needs.build-context.outputs.run-tests == 'true'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
bolt:
|
||||
- false
|
||||
- true
|
||||
free-threading:
|
||||
- false
|
||||
- true
|
||||
os:
|
||||
- ubuntu-24.04
|
||||
- ubuntu-24.04-arm
|
||||
exclude:
|
||||
# Do not test BOLT with free-threading, to conserve resources
|
||||
- bolt: true
|
||||
free-threading: true
|
||||
# BOLT currently crashes during instrumentation on aarch64
|
||||
- os: ubuntu-24.04-arm
|
||||
bolt: true
|
||||
uses: ./.github/workflows/reusable-ubuntu.yml
|
||||
with:
|
||||
config_hash: ${{ needs.check_source.outputs.config_hash }}
|
||||
config_hash: ${{ needs.build-context.outputs.config-hash }}
|
||||
bolt-optimizations: ${{ matrix.bolt }}
|
||||
free-threading: ${{ matrix.free-threading }}
|
||||
os: ${{ matrix.os }}
|
||||
|
||||
build_ubuntu_ssltests:
|
||||
build-ubuntu-ssltests:
|
||||
name: 'Ubuntu SSL tests with OpenSSL'
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 60
|
||||
needs: check_source
|
||||
if: needs.check_source.outputs.run_tests == 'true'
|
||||
needs: build-context
|
||||
if: needs.build-context.outputs.run-tests == 'true'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-24.04]
|
||||
openssl_ver: [3.0.15, 3.1.7, 3.2.3, 3.3.2]
|
||||
openssl_ver: [3.0.16, 3.1.8, 3.2.4, 3.3.3, 3.4.1]
|
||||
# See Tools/ssl/make_ssl_data.py for notes on adding a new version
|
||||
env:
|
||||
OPENSSL_VER: ${{ matrix.openssl_ver }}
|
||||
MULTISSL_DIR: ${{ github.workspace }}/multissl
|
||||
|
|
@ -258,16 +279,18 @@ jobs:
|
|||
LD_LIBRARY_PATH: ${{ github.workspace }}/multissl/openssl/${{ matrix.openssl_ver }}/lib
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Runner image version
|
||||
run: echo "IMAGE_VERSION=${ImageVersion}" >> "$GITHUB_ENV"
|
||||
run: echo "IMAGE_OS_VERSION=${ImageOS}-${ImageVersion}" >> "$GITHUB_ENV"
|
||||
- name: Restore config.cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: config.cache
|
||||
key: ${{ github.job }}-${{ runner.os }}-${{ env.IMAGE_VERSION }}-${{ needs.check_source.outputs.config_hash }}
|
||||
key: ${{ github.job }}-${{ env.IMAGE_OS_VERSION }}-${{ needs.build-context.outputs.config-hash }}
|
||||
- name: Register gcc problem matcher
|
||||
run: echo "::add-matcher::.github/problem-matchers/gcc.json"
|
||||
- name: Install Dependencies
|
||||
- name: Install dependencies
|
||||
run: sudo ./.github/workflows/posix-deps-apt.sh
|
||||
- name: Configure OpenSSL env vars
|
||||
run: |
|
||||
|
|
@ -299,28 +322,30 @@ jobs:
|
|||
- name: SSL tests
|
||||
run: ./python Lib/test/ssltests.py
|
||||
|
||||
build_wasi:
|
||||
build-wasi:
|
||||
name: 'WASI'
|
||||
needs: check_source
|
||||
if: needs.check_source.outputs.run_tests == 'true'
|
||||
needs: build-context
|
||||
if: needs.build-context.outputs.run-tests == 'true'
|
||||
uses: ./.github/workflows/reusable-wasi.yml
|
||||
with:
|
||||
config_hash: ${{ needs.check_source.outputs.config_hash }}
|
||||
config_hash: ${{ needs.build-context.outputs.config-hash }}
|
||||
|
||||
test_hypothesis:
|
||||
test-hypothesis:
|
||||
name: "Hypothesis tests on Ubuntu"
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 60
|
||||
needs: check_source
|
||||
if: needs.check_source.outputs.run_tests == 'true' && needs.check_source.outputs.run_hypothesis == 'true'
|
||||
needs: build-context
|
||||
if: needs.build-context.outputs.run-tests == 'true'
|
||||
env:
|
||||
OPENSSL_VER: 3.0.15
|
||||
OPENSSL_VER: 3.0.16
|
||||
PYTHONSTRICTEXTENSIONBUILD: 1
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Register gcc problem matcher
|
||||
run: echo "::add-matcher::.github/problem-matchers/gcc.json"
|
||||
- name: Install Dependencies
|
||||
- name: Install dependencies
|
||||
run: sudo ./.github/workflows/posix-deps-apt.sh
|
||||
- name: Configure OpenSSL env vars
|
||||
run: |
|
||||
|
|
@ -352,12 +377,12 @@ jobs:
|
|||
- name: Bind mount sources read-only
|
||||
run: sudo mount --bind -o ro "$GITHUB_WORKSPACE" "$CPYTHON_RO_SRCDIR"
|
||||
- name: Runner image version
|
||||
run: echo "IMAGE_VERSION=${ImageVersion}" >> "$GITHUB_ENV"
|
||||
run: echo "IMAGE_OS_VERSION=${ImageOS}-${ImageVersion}" >> "$GITHUB_ENV"
|
||||
- name: Restore config.cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ env.CPYTHON_BUILDDIR }}/config.cache
|
||||
key: ${{ github.job }}-${{ runner.os }}-${{ env.IMAGE_VERSION }}-${{ needs.check_source.outputs.config_hash }}
|
||||
key: ${{ github.job }}-${{ env.IMAGE_OS_VERSION }}-${{ needs.build-context.outputs.config-hash }}
|
||||
- name: Configure CPython out-of-tree
|
||||
working-directory: ${{ env.CPYTHON_BUILDDIR }}
|
||||
run: |
|
||||
|
|
@ -401,10 +426,11 @@ jobs:
|
|||
#
|
||||
# (GH-104097) test_sysconfig is skipped because it has tests that are
|
||||
# failing when executed from inside a virtual environment.
|
||||
${{ env.VENV_PYTHON }} -m test \
|
||||
"${VENV_PYTHON}" -m test \
|
||||
-W \
|
||||
-o \
|
||||
--slowest \
|
||||
-j4 \
|
||||
--timeout 900 \
|
||||
-x test_asyncio \
|
||||
-x test_multiprocessing_fork \
|
||||
-x test_multiprocessing_forkserver \
|
||||
|
|
@ -420,32 +446,34 @@ jobs:
|
|||
name: hypothesis-example-db
|
||||
path: ${{ env.CPYTHON_BUILDDIR }}/.hypothesis/examples/
|
||||
|
||||
|
||||
build_asan:
|
||||
build-asan:
|
||||
name: 'Address sanitizer'
|
||||
runs-on: ${{ matrix.os }}
|
||||
timeout-minutes: 60
|
||||
needs: check_source
|
||||
if: needs.check_source.outputs.run_tests == 'true'
|
||||
needs: build-context
|
||||
if: needs.build-context.outputs.run-tests == 'true'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-24.04]
|
||||
env:
|
||||
OPENSSL_VER: 3.0.15
|
||||
OPENSSL_VER: 3.0.16
|
||||
PYTHONSTRICTEXTENSIONBUILD: 1
|
||||
ASAN_OPTIONS: detect_leaks=0:allocator_may_return_null=1:handle_segv=0
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Runner image version
|
||||
run: echo "IMAGE_VERSION=${ImageVersion}" >> "$GITHUB_ENV"
|
||||
run: echo "IMAGE_OS_VERSION=${ImageOS}-${ImageVersion}" >> "$GITHUB_ENV"
|
||||
- name: Restore config.cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: config.cache
|
||||
key: ${{ github.job }}-${{ runner.os }}-${{ env.IMAGE_VERSION }}-${{ needs.check_source.outputs.config_hash }}
|
||||
key: ${{ github.job }}-${{ env.IMAGE_OS_VERSION }}-${{ needs.build-context.outputs.config-hash }}
|
||||
- name: Register gcc problem matcher
|
||||
run: echo "::add-matcher::.github/problem-matchers/gcc.json"
|
||||
- name: Install Dependencies
|
||||
- name: Install dependencies
|
||||
run: sudo ./.github/workflows/posix-deps-apt.sh
|
||||
- name: Set up GCC-10 for ASAN
|
||||
uses: egor-tensin/setup-gcc@v1
|
||||
|
|
@ -482,35 +510,70 @@ jobs:
|
|||
- name: Tests
|
||||
run: xvfb-run make ci
|
||||
|
||||
build_tsan:
|
||||
name: 'Thread sanitizer'
|
||||
needs: check_source
|
||||
if: needs.check_source.outputs.run_tests == 'true'
|
||||
build-tsan:
|
||||
name: >-
|
||||
Thread sanitizer
|
||||
${{ fromJSON(matrix.free-threading) && '(free-threading)' || '' }}
|
||||
needs: build-context
|
||||
if: needs.build-context.outputs.run-tests == 'true'
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
free-threading:
|
||||
- false
|
||||
- true
|
||||
uses: ./.github/workflows/reusable-tsan.yml
|
||||
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
|
||||
config_hash: ${{ needs.build-context.outputs.config-hash }}
|
||||
free-threading: ${{ matrix.free-threading }}
|
||||
|
||||
build_tsan_free_threading:
|
||||
name: 'Thread sanitizer (free-threading)'
|
||||
needs: check_source
|
||||
if: needs.check_source.outputs.run_tests == 'true'
|
||||
uses: ./.github/workflows/reusable-tsan.yml
|
||||
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
|
||||
cross-build-linux:
|
||||
name: Cross build Linux
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
needs: build-context
|
||||
if: needs.build-context.outputs.run-tests == 'true'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Runner image version
|
||||
run: echo "IMAGE_OS_VERSION=${ImageOS}-${ImageVersion}" >> "$GITHUB_ENV"
|
||||
- name: Restore config.cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: config.cache
|
||||
key: ${{ github.job }}-${{ env.IMAGE_OS_VERSION }}-${{ needs.build-context.outputs.config-hash }}
|
||||
- name: Register gcc problem matcher
|
||||
run: echo "::add-matcher::.github/problem-matchers/gcc.json"
|
||||
- name: Set build dir
|
||||
run:
|
||||
# an absolute path outside of the working directoy
|
||||
echo "BUILD_DIR=$(realpath ${{ github.workspace }}/../build)" >> "$GITHUB_ENV"
|
||||
- name: Install dependencies
|
||||
run: sudo ./.github/workflows/posix-deps-apt.sh
|
||||
- name: Configure host build
|
||||
run: ./configure --prefix="$BUILD_DIR/host-python"
|
||||
- name: Install host Python
|
||||
run: make -j8 install
|
||||
- name: Run test subset with host build
|
||||
run: |
|
||||
"$BUILD_DIR/host-python/bin/python3" -m test test_sysconfig test_site test_embed
|
||||
- name: Configure cross build
|
||||
run: ./configure --prefix="$BUILD_DIR/cross-python" --with-build-python="$BUILD_DIR/host-python/bin/python3"
|
||||
- name: Install cross Python
|
||||
run: make -j8 install
|
||||
- name: Run test subset with host build
|
||||
run: |
|
||||
"$BUILD_DIR/cross-python/bin/python3" -m test test_sysconfig test_site test_embed
|
||||
|
||||
# CIFuzz job based on https://google.github.io/oss-fuzz/getting-started/continuous-integration/
|
||||
cifuzz:
|
||||
name: CIFuzz
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
needs: check_source
|
||||
if: needs.check_source.outputs.run_cifuzz == 'true'
|
||||
needs: build-context
|
||||
if: needs.build-context.outputs.run-ci-fuzz == 'true'
|
||||
permissions:
|
||||
security-events: write
|
||||
strategy:
|
||||
|
|
@ -532,8 +595,8 @@ jobs:
|
|||
output-sarif: true
|
||||
sanitizer: ${{ matrix.sanitizer }}
|
||||
- name: Upload crash
|
||||
uses: actions/upload-artifact@v4
|
||||
if: failure() && steps.build.outcome == 'success'
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ matrix.sanitizer }}-artifacts
|
||||
path: ./out/artifacts
|
||||
|
|
@ -546,72 +609,71 @@ jobs:
|
|||
|
||||
all-required-green: # This job does nothing and is only used for the branch protection
|
||||
name: All required checks pass
|
||||
if: always()
|
||||
|
||||
needs:
|
||||
- check_source # Transitive dependency, needed to access `run_tests` value
|
||||
- check-docs
|
||||
- check_autoconf_regen
|
||||
- check_generated_files
|
||||
- build_macos
|
||||
- build_ubuntu
|
||||
- build_ubuntu_ssltests
|
||||
- build_wasi
|
||||
- build_windows
|
||||
- build_windows_msi
|
||||
- test_hypothesis
|
||||
- build_asan
|
||||
- build_tsan
|
||||
- build_tsan_free_threading
|
||||
- cifuzz
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
needs:
|
||||
- build-context # Transitive dependency, needed to access `run-tests` value
|
||||
- check-docs
|
||||
- check-autoconf-regen
|
||||
- check-generated-files
|
||||
- build-windows
|
||||
- build-windows-msi
|
||||
- build-macos
|
||||
- build-ubuntu
|
||||
- build-ubuntu-ssltests
|
||||
- build-wasi
|
||||
- test-hypothesis
|
||||
- build-asan
|
||||
- build-tsan
|
||||
- cross-build-linux
|
||||
- cifuzz
|
||||
if: always()
|
||||
|
||||
steps:
|
||||
- name: Check whether the needed jobs succeeded or failed
|
||||
uses: re-actors/alls-green@05ac9388f0aebcb5727afa17fcccfecd6f8ec5fe
|
||||
with:
|
||||
allowed-failures: >-
|
||||
build_ubuntu_ssltests,
|
||||
build_windows_msi,
|
||||
build-windows-msi,
|
||||
build-ubuntu-ssltests,
|
||||
test-hypothesis,
|
||||
cifuzz,
|
||||
test_hypothesis,
|
||||
allowed-skips: >-
|
||||
${{
|
||||
!fromJSON(needs.check_source.outputs.run-docs)
|
||||
!fromJSON(needs.build-context.outputs.run-docs)
|
||||
&& '
|
||||
check-docs,
|
||||
'
|
||||
|| ''
|
||||
}}
|
||||
${{
|
||||
needs.check_source.outputs.run_tests != 'true'
|
||||
needs.build-context.outputs.run-tests != 'true'
|
||||
&& '
|
||||
check_autoconf_regen,
|
||||
check_generated_files,
|
||||
build_macos,
|
||||
build_ubuntu,
|
||||
build_ubuntu_ssltests,
|
||||
build_wasi,
|
||||
build_windows,
|
||||
build_asan,
|
||||
build_tsan,
|
||||
build_tsan_free_threading,
|
||||
check-autoconf-regen,
|
||||
check-generated-files,
|
||||
build-macos,
|
||||
build-ubuntu,
|
||||
build-ubuntu-ssltests,
|
||||
build-wasi,
|
||||
test-hypothesis,
|
||||
build-asan,
|
||||
build-tsan,
|
||||
cross-build-linux,
|
||||
'
|
||||
|| ''
|
||||
}}
|
||||
${{
|
||||
!fromJSON(needs.check_source.outputs.run_cifuzz)
|
||||
!fromJSON(needs.build-context.outputs.run-windows-tests)
|
||||
&& '
|
||||
build-windows,
|
||||
'
|
||||
|| ''
|
||||
}}
|
||||
${{
|
||||
!fromJSON(needs.build-context.outputs.run-ci-fuzz)
|
||||
&& '
|
||||
cifuzz,
|
||||
'
|
||||
|| ''
|
||||
}}
|
||||
${{
|
||||
!fromJSON(needs.check_source.outputs.run_hypothesis)
|
||||
&& '
|
||||
test_hypothesis,
|
||||
'
|
||||
|| ''
|
||||
}}
|
||||
jobs: ${{ toJSON(needs) }}
|
||||
|
|
|
|||
7
.github/workflows/documentation-links.yml
vendored
7
.github/workflows/documentation-links.yml
vendored
|
|
@ -10,9 +10,6 @@ on:
|
|||
- 'Doc/**'
|
||||
- '.github/workflows/doc.yml'
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
|
@ -20,6 +17,10 @@ concurrency:
|
|||
jobs:
|
||||
documentation-links:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
timeout-minutes: 5
|
||||
|
||||
steps:
|
||||
- uses: readthedocs/actions/preview@v1
|
||||
with:
|
||||
|
|
|
|||
131
.github/workflows/jit.yml
vendored
131
.github/workflows/jit.yml
vendored
|
|
@ -25,13 +25,18 @@ concurrency:
|
|||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
FORCE_COLOR: 1
|
||||
|
||||
jobs:
|
||||
interpreter:
|
||||
name: Interpreter (Debug)
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 90
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Build tier two interpreter
|
||||
run: |
|
||||
./configure --enable-experimental-jit=interpreter --with-pydebug
|
||||
|
|
@ -54,9 +59,7 @@ jobs:
|
|||
- x86_64-apple-darwin/clang
|
||||
- aarch64-apple-darwin/clang
|
||||
- x86_64-unknown-linux-gnu/gcc
|
||||
- x86_64-unknown-linux-gnu/clang
|
||||
- aarch64-unknown-linux-gnu/gcc
|
||||
- aarch64-unknown-linux-gnu/clang
|
||||
debug:
|
||||
- true
|
||||
- false
|
||||
|
|
@ -66,125 +69,87 @@ jobs:
|
|||
- target: i686-pc-windows-msvc/msvc
|
||||
architecture: Win32
|
||||
runner: windows-latest
|
||||
compiler: msvc
|
||||
- target: x86_64-pc-windows-msvc/msvc
|
||||
architecture: x64
|
||||
runner: windows-latest
|
||||
compiler: msvc
|
||||
- target: aarch64-pc-windows-msvc/msvc
|
||||
architecture: ARM64
|
||||
runner: windows-latest
|
||||
compiler: msvc
|
||||
runner: windows-11-arm
|
||||
- target: x86_64-apple-darwin/clang
|
||||
architecture: x86_64
|
||||
runner: macos-13
|
||||
compiler: clang
|
||||
- target: aarch64-apple-darwin/clang
|
||||
architecture: aarch64
|
||||
runner: macos-14
|
||||
compiler: clang
|
||||
- target: x86_64-unknown-linux-gnu/gcc
|
||||
architecture: x86_64
|
||||
runner: ubuntu-22.04
|
||||
compiler: gcc
|
||||
- target: x86_64-unknown-linux-gnu/clang
|
||||
architecture: x86_64
|
||||
runner: ubuntu-22.04
|
||||
compiler: clang
|
||||
runner: ubuntu-24.04
|
||||
- target: aarch64-unknown-linux-gnu/gcc
|
||||
architecture: aarch64
|
||||
runner: ubuntu-22.04
|
||||
compiler: gcc
|
||||
- target: aarch64-unknown-linux-gnu/clang
|
||||
architecture: aarch64
|
||||
runner: ubuntu-22.04
|
||||
compiler: clang
|
||||
env:
|
||||
CC: ${{ matrix.compiler }}
|
||||
runner: ubuntu-24.04-arm
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Native Windows
|
||||
if: runner.os == 'Windows' && matrix.architecture != 'ARM64'
|
||||
# PCbuild downloads LLVM automatically:
|
||||
- name: Windows
|
||||
if: runner.os == 'Windows'
|
||||
run: |
|
||||
choco install llvm --allow-downgrade --no-progress --version ${{ matrix.llvm }}.1.0
|
||||
./PCbuild/build.bat --experimental-jit ${{ matrix.debug && '-d' || '--pgo' }} -p ${{ matrix.architecture }}
|
||||
./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 install llvm --allow-downgrade --no-progress --version ${{ matrix.llvm }}.1.0
|
||||
./PCbuild/build.bat --experimental-jit ${{ matrix.debug && '-d' || '' }} -p ${{ matrix.architecture }}
|
||||
./PCbuild/rt.bat ${{ matrix.debug && '-d' || '' }} -p ${{ matrix.architecture }} -q --multiprocess 0 --timeout 4500 --verbose2 --verbose3
|
||||
|
||||
# The `find` line is required as a result of https://github.com/actions/runner-images/issues/9966.
|
||||
# This is a bug in the macOS runner image where the pre-installed Python is installed in the same
|
||||
# directory as the Homebrew Python, which causes the build to fail for macos-13. This line removes
|
||||
# directory as the Homebrew Python, which causes the build to fail for macos-13. This line removes
|
||||
# the symlink to the pre-installed Python so that the Homebrew Python is used instead.
|
||||
- name: Native macOS
|
||||
- name: macOS
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
brew update
|
||||
find /usr/local/bin -lname '*/Library/Frameworks/Python.framework/*' -delete
|
||||
brew install llvm@${{ matrix.llvm }}
|
||||
export SDKROOT="$(xcrun --show-sdk-path)"
|
||||
./configure --enable-experimental-jit ${{ matrix.debug && '--with-pydebug' || '--enable-optimizations --with-lto' }}
|
||||
./configure --enable-experimental-jit --enable-universalsdk --with-universal-archs=universal2 ${{ matrix.debug && '--with-pydebug' || '' }}
|
||||
make all --jobs 4
|
||||
./python.exe -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3
|
||||
|
||||
- name: Native Linux
|
||||
if: runner.os == 'Linux' && matrix.architecture == 'x86_64'
|
||||
- name: Linux
|
||||
if: runner.os == '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"
|
||||
./configure --enable-experimental-jit ${{ matrix.debug && '--with-pydebug' || '--enable-optimizations --with-lto' }}
|
||||
./configure --enable-experimental-jit ${{ matrix.debug && '--with-pydebug' || '' }}
|
||||
make all --jobs 4
|
||||
./python -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3
|
||||
|
||||
- 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"
|
||||
./configure --prefix="$(pwd)/../build"
|
||||
make install --jobs 4
|
||||
make clean --jobs 4
|
||||
export HOST=${{ matrix.architecture }}-linux-gnu
|
||||
sudo apt install --yes "gcc-$HOST" qemu-user
|
||||
${{ !matrix.debug && matrix.compiler == 'clang' && './configure --enable-optimizations' || '' }}
|
||||
${{ !matrix.debug && matrix.compiler == 'clang' && 'make profile-run-stamp --jobs 4' || '' }}
|
||||
export QEMU_LD_PREFIX="/usr/$HOST"
|
||||
CC="${{ matrix.compiler == 'clang' && 'clang --target=$HOST' || '$HOST-gcc' }}" \
|
||||
CPP="$CC --preprocess" \
|
||||
HOSTRUNNER=qemu-${{ matrix.architecture }} \
|
||||
./configure --enable-experimental-jit ${{ matrix.debug && '--with-pydebug' || '--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
|
||||
make all --jobs 4
|
||||
./python -m test --ignorefile=Tools/jit/ignore-tests-emulated-linux.txt --multiprocess 0 --timeout 4500 --verbose2 --verbose3
|
||||
|
||||
jit-with-disabled-gil:
|
||||
name: Free-Threaded (Debug)
|
||||
needs: interpreter
|
||||
runs-on: ubuntu-22.04
|
||||
strategy:
|
||||
matrix:
|
||||
llvm:
|
||||
- 19
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
- name: Build with JIT enabled and GIL disabled
|
||||
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 --with-pydebug --disable-gil
|
||||
make all --jobs 4
|
||||
- name: Run tests
|
||||
run: |
|
||||
./python -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3
|
||||
# XXX: GH-133171
|
||||
# jit-with-disabled-gil:
|
||||
# name: Free-Threaded (Debug)
|
||||
# needs: interpreter
|
||||
# runs-on: ubuntu-24.04
|
||||
# timeout-minutes: 90
|
||||
# strategy:
|
||||
# fail-fast: false
|
||||
# matrix:
|
||||
# llvm:
|
||||
# - 19
|
||||
# steps:
|
||||
# - uses: actions/checkout@v4
|
||||
# with:
|
||||
# persist-credentials: false
|
||||
# - uses: actions/setup-python@v5
|
||||
# with:
|
||||
# python-version: '3.11'
|
||||
# - name: Build with JIT enabled and GIL disabled
|
||||
# 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 --with-pydebug --disable-gil
|
||||
# make all --jobs 4
|
||||
# - name: Run tests
|
||||
# run: |
|
||||
# ./python -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3
|
||||
|
|
|
|||
2
.github/workflows/lint.yml
vendored
2
.github/workflows/lint.yml
vendored
|
|
@ -20,6 +20,8 @@ jobs:
|
|||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.x"
|
||||
|
|
|
|||
21
.github/workflows/mypy.yml
vendored
21
.github/workflows/mypy.yml
vendored
|
|
@ -8,15 +8,23 @@ on:
|
|||
pull_request:
|
||||
paths:
|
||||
- ".github/workflows/mypy.yml"
|
||||
- "Lib/_colorize.py"
|
||||
- "Lib/_pyrepl/**"
|
||||
- "Lib/test/libregrtest/**"
|
||||
- "Lib/tomllib/**"
|
||||
- "Misc/mypy/**"
|
||||
- "Tools/build/compute-changes.py"
|
||||
- "Tools/build/deepfreeze.py"
|
||||
- "Tools/build/generate_sbom.py"
|
||||
- "Tools/build/generate-build-details.py"
|
||||
- "Tools/build/verify_ensurepip_wheels.py"
|
||||
- "Tools/build/update_file.py"
|
||||
- "Tools/build/umarshal.py"
|
||||
- "Tools/cases_generator/**"
|
||||
- "Tools/clinic/**"
|
||||
- "Tools/jit/**"
|
||||
- "Tools/peg_generator/**"
|
||||
- "Tools/requirements-dev.txt"
|
||||
- "Tools/wasm/**"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
|
|
@ -33,28 +41,31 @@ concurrency:
|
|||
|
||||
jobs:
|
||||
mypy:
|
||||
name: Run mypy on ${{ matrix.target }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
target: [
|
||||
"Lib/_pyrepl",
|
||||
"Lib/test/libregrtest",
|
||||
"Lib/tomllib",
|
||||
"Tools/build",
|
||||
"Tools/cases_generator",
|
||||
"Tools/clinic",
|
||||
"Tools/jit",
|
||||
"Tools/peg_generator",
|
||||
"Tools/wasm",
|
||||
]
|
||||
name: Run mypy on ${{ matrix.target }}
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3.13"
|
||||
cache: pip
|
||||
cache-dependency-path: Tools/requirements-dev.txt
|
||||
- run: pip install -r Tools/requirements-dev.txt
|
||||
- run: python3 Misc/mypy/make_symlinks.py --symlink
|
||||
- run: mypy --config-file ${{ matrix.target }}/mypy.ini
|
||||
|
|
|
|||
1
.github/workflows/project-updater.yml
vendored
1
.github/workflows/project-updater.yml
vendored
|
|
@ -15,6 +15,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
# if an issue has any of these labels, it will be added
|
||||
|
|
|
|||
8
.github/workflows/require-pr-label.yml
vendored
8
.github/workflows/require-pr-label.yml
vendored
|
|
@ -4,15 +4,13 @@ on:
|
|||
pull_request:
|
||||
types: [opened, reopened, labeled, unlabeled, synchronize]
|
||||
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
label-dnm:
|
||||
name: DO-NOT-MERGE
|
||||
if: github.repository_owner == 'python'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: read
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
|
|
@ -28,6 +26,8 @@ jobs:
|
|||
name: Unresolved review
|
||||
if: github.repository_owner == 'python'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: read
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
|
|
|
|||
156
.github/workflows/reusable-change-detection.yml
vendored
156
.github/workflows/reusable-change-detection.yml
vendored
|
|
@ -1,156 +0,0 @@
|
|||
name: Reusable change detection
|
||||
|
||||
on: # yamllint disable-line rule:truthy
|
||||
workflow_call:
|
||||
outputs:
|
||||
# Some of the referenced steps set outputs conditionally and there may be
|
||||
# cases when referencing them evaluates to empty strings. It is nice to
|
||||
# work with proper booleans so they have to be evaluated through JSON
|
||||
# conversion in the expressions. However, empty strings used like that
|
||||
# may trigger all sorts of undefined and hard-to-debug behaviors in
|
||||
# GitHub Actions CI/CD. To help with this, all of the outputs set here
|
||||
# that are meant to be used as boolean flags (and not arbitrary strings),
|
||||
# MUST have fallbacks with default values set. A common pattern would be
|
||||
# to add ` || false` to all such expressions here, in the output
|
||||
# definitions. They can then later be safely used through the following
|
||||
# idiom in job conditionals and other expressions. Here's some examples:
|
||||
#
|
||||
# if: fromJSON(needs.change-detection.outputs.run-docs)
|
||||
#
|
||||
# ${{
|
||||
# fromJSON(needs.change-detection.outputs.run-tests)
|
||||
# && 'truthy-branch'
|
||||
# || 'falsy-branch'
|
||||
# }}
|
||||
#
|
||||
config_hash:
|
||||
description: Config hash value for use in cache keys
|
||||
value: ${{ jobs.compute-changes.outputs.config-hash }} # str
|
||||
run-docs:
|
||||
description: Whether to build the docs
|
||||
value: ${{ jobs.compute-changes.outputs.run-docs || false }} # bool
|
||||
run_tests:
|
||||
description: Whether to run the regular tests
|
||||
value: ${{ jobs.compute-changes.outputs.run-tests || false }} # bool
|
||||
run-win-msi:
|
||||
description: Whether to run the MSI installer smoke tests
|
||||
value: >- # bool
|
||||
${{ jobs.compute-changes.outputs.run-win-msi || false }}
|
||||
run_hypothesis:
|
||||
description: Whether to run the Hypothesis tests
|
||||
value: >- # bool
|
||||
${{ jobs.compute-changes.outputs.run-hypothesis || false }}
|
||||
run_cifuzz:
|
||||
description: Whether to run the CIFuzz job
|
||||
value: >- # bool
|
||||
${{ jobs.compute-changes.outputs.run-cifuzz || false }}
|
||||
|
||||
jobs:
|
||||
compute-changes:
|
||||
name: Compute changed files
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
outputs:
|
||||
config-hash: ${{ steps.config-hash.outputs.hash }}
|
||||
run-cifuzz: ${{ steps.check.outputs.run-cifuzz }}
|
||||
run-docs: ${{ steps.docs-changes.outputs.run-docs }}
|
||||
run-hypothesis: ${{ steps.check.outputs.run-hypothesis }}
|
||||
run-tests: ${{ steps.check.outputs.run-tests }}
|
||||
run-win-msi: ${{ steps.win-msi-changes.outputs.run-win-msi }}
|
||||
steps:
|
||||
- run: >-
|
||||
echo '${{ github.event_name }}'
|
||||
- uses: actions/checkout@v4
|
||||
- name: Check for source changes
|
||||
id: check
|
||||
run: |
|
||||
if [ -z "$GITHUB_BASE_REF" ]; then
|
||||
echo "run-tests=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
git fetch origin "$GITHUB_BASE_REF" --depth=1
|
||||
# git diff "origin/$GITHUB_BASE_REF..." (3 dots) may be more
|
||||
# reliable than git diff "origin/$GITHUB_BASE_REF.." (2 dots),
|
||||
# but it requires to download more commits (this job uses
|
||||
# "git fetch --depth=1").
|
||||
#
|
||||
# git diff "origin/$GITHUB_BASE_REF..." (3 dots) works with Git
|
||||
# 2.26, but Git 2.28 is stricter and fails with "no merge base".
|
||||
#
|
||||
# git diff "origin/$GITHUB_BASE_REF.." (2 dots) should be enough on
|
||||
# GitHub, since GitHub starts by merging origin/$GITHUB_BASE_REF
|
||||
# into the PR branch anyway.
|
||||
#
|
||||
# https://github.com/python/core-workflow/issues/373
|
||||
git diff --name-only "origin/$GITHUB_BASE_REF.." | grep -qvE '(\.rst$|^Doc|^Misc|^\.pre-commit-config\.yaml$|\.ruff\.toml$|\.md$|mypy\.ini$)' && echo "run-tests=true" >> "$GITHUB_OUTPUT" || true
|
||||
fi
|
||||
|
||||
# Check if we should run hypothesis tests
|
||||
GIT_BRANCH=${GITHUB_BASE_REF:-${GITHUB_REF#refs/heads/}}
|
||||
echo "$GIT_BRANCH"
|
||||
if $(echo "$GIT_BRANCH" | grep -q -w '3\.\(8\|9\|10\|11\)'); then
|
||||
echo "Branch too old for hypothesis tests"
|
||||
echo "run-hypothesis=false" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "Run hypothesis tests"
|
||||
echo "run-hypothesis=true" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
|
||||
# oss-fuzz maintains a configuration for fuzzing the main branch of
|
||||
# CPython, so CIFuzz should be run only for code that is likely to be
|
||||
# merged into the main branch; compatibility with older branches may
|
||||
# be broken.
|
||||
FUZZ_RELEVANT_FILES='(\.c$|\.h$|\.cpp$|^configure$|^\.github/workflows/build\.yml$|^Modules/_xxtestfuzz)'
|
||||
if [ "$GITHUB_BASE_REF" = "main" ] && [ "$(git diff --name-only "origin/$GITHUB_BASE_REF.." | grep -qE $FUZZ_RELEVANT_FILES; echo $?)" -eq 0 ]; then
|
||||
# The tests are pretty slow so they are executed only for PRs
|
||||
# changing relevant files.
|
||||
echo "Run CIFuzz tests"
|
||||
echo "run-cifuzz=true" >> "$GITHUB_OUTPUT"
|
||||
else
|
||||
echo "Branch too old for CIFuzz tests; or no C files were changed"
|
||||
echo "run-cifuzz=false" >> "$GITHUB_OUTPUT"
|
||||
fi
|
||||
- name: Compute hash for config cache key
|
||||
id: config-hash
|
||||
run: |
|
||||
echo "hash=${{ hashFiles('configure', 'configure.ac', '.github/workflows/build.yml') }}" >> "$GITHUB_OUTPUT"
|
||||
- name: Get a list of the changed documentation-related files
|
||||
if: github.event_name == 'pull_request'
|
||||
id: changed-docs-files
|
||||
uses: Ana06/get-changed-files@v2.3.0
|
||||
with:
|
||||
filter: |
|
||||
Doc/**
|
||||
Misc/**
|
||||
.github/workflows/reusable-docs.yml
|
||||
format: csv # works for paths with spaces
|
||||
- name: Check for docs changes
|
||||
# We only want to run this on PRs when related files are changed,
|
||||
# or when user triggers manual workflow run.
|
||||
if: >-
|
||||
(
|
||||
github.event_name == 'pull_request'
|
||||
&& steps.changed-docs-files.outputs.added_modified_renamed != ''
|
||||
) || github.event_name == 'workflow_dispatch'
|
||||
id: docs-changes
|
||||
run: |
|
||||
echo "run-docs=true" >> "${GITHUB_OUTPUT}"
|
||||
- name: Get a list of the MSI installer-related files
|
||||
if: github.event_name == 'pull_request'
|
||||
id: changed-win-msi-files
|
||||
uses: Ana06/get-changed-files@v2.3.0
|
||||
with:
|
||||
filter: |
|
||||
Tools/msi/**
|
||||
.github/workflows/reusable-windows-msi.yml
|
||||
format: csv # works for paths with spaces
|
||||
- name: Check for changes in MSI installer-related files
|
||||
# We only want to run this on PRs when related files are changed,
|
||||
# or when user triggers manual workflow run.
|
||||
if: >-
|
||||
(
|
||||
github.event_name == 'pull_request'
|
||||
&& steps.changed-win-msi-files.outputs.added_modified_renamed != ''
|
||||
) || github.event_name == 'workflow_dispatch'
|
||||
id: win-msi-changes
|
||||
run: |
|
||||
echo "run-win-msi=true" >> "${GITHUB_OUTPUT}"
|
||||
107
.github/workflows/reusable-context.yml
vendored
Normal file
107
.github/workflows/reusable-context.yml
vendored
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
name: Reusable build context
|
||||
|
||||
on: # yamllint disable-line rule:truthy
|
||||
workflow_call:
|
||||
outputs:
|
||||
# Every referenced step MUST always set its output variable,
|
||||
# either via ``Tools/build/compute-changes.py`` or in this workflow file.
|
||||
# Boolean outputs (generally prefixed ``run-``) can then later be used
|
||||
# safely through the following idiom in job conditionals and other
|
||||
# expressions. Here's some examples:
|
||||
#
|
||||
# if: fromJSON(needs.build-context.outputs.run-tests)
|
||||
#
|
||||
# ${{
|
||||
# fromJSON(needs.build-context.outputs.run-tests)
|
||||
# && 'truthy-branch'
|
||||
# || 'falsy-branch'
|
||||
# }}
|
||||
#
|
||||
config-hash:
|
||||
description: Config hash value for use in cache keys
|
||||
value: ${{ jobs.compute-changes.outputs.config-hash }} # str
|
||||
run-docs:
|
||||
description: Whether to build the docs
|
||||
value: ${{ jobs.compute-changes.outputs.run-docs }} # bool
|
||||
run-tests:
|
||||
description: Whether to run the regular tests
|
||||
value: ${{ jobs.compute-changes.outputs.run-tests }} # bool
|
||||
run-windows-tests:
|
||||
description: Whether to run the Windows tests
|
||||
value: ${{ jobs.compute-changes.outputs.run-windows-tests }} # bool
|
||||
run-windows-msi:
|
||||
description: Whether to run the MSI installer smoke tests
|
||||
value: ${{ jobs.compute-changes.outputs.run-windows-msi }} # bool
|
||||
run-ci-fuzz:
|
||||
description: Whether to run the CIFuzz job
|
||||
value: ${{ jobs.compute-changes.outputs.run-ci-fuzz }} # bool
|
||||
|
||||
jobs:
|
||||
compute-changes:
|
||||
name: Create context from changed files
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
outputs:
|
||||
config-hash: ${{ steps.config-hash.outputs.hash }}
|
||||
run-ci-fuzz: ${{ steps.changes.outputs.run-ci-fuzz }}
|
||||
run-docs: ${{ steps.changes.outputs.run-docs }}
|
||||
run-tests: ${{ steps.changes.outputs.run-tests }}
|
||||
run-windows-msi: ${{ steps.changes.outputs.run-windows-msi }}
|
||||
run-windows-tests: ${{ steps.changes.outputs.run-windows-tests }}
|
||||
steps:
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: "3"
|
||||
|
||||
- run: >-
|
||||
echo '${{ github.event_name }}'
|
||||
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: >-
|
||||
${{
|
||||
github.event_name == 'pull_request'
|
||||
&& github.event.pull_request.head.sha
|
||||
|| ''
|
||||
}}
|
||||
|
||||
# Adapted from https://github.com/actions/checkout/issues/520#issuecomment-1167205721
|
||||
- name: Fetch commits to get branch diff
|
||||
if: github.event_name == 'pull_request'
|
||||
run: |
|
||||
set -eux
|
||||
|
||||
# Fetch enough history to find a common ancestor commit (aka merge-base):
|
||||
git fetch origin "${refspec_pr}" --depth=$(( commits + 1 )) \
|
||||
--no-tags --prune --no-recurse-submodules
|
||||
|
||||
# This should get the oldest commit in the local fetched history (which may not be the commit the PR branched from):
|
||||
COMMON_ANCESTOR=$( git rev-list --first-parent --max-parents=0 --max-count=1 "${branch_pr}" )
|
||||
DATE=$( git log --date=iso8601 --format=%cd "${COMMON_ANCESTOR}" )
|
||||
|
||||
# Get all commits since that commit date from the base branch (eg: main):
|
||||
git fetch origin "${refspec_base}" --shallow-since="${DATE}" \
|
||||
--no-tags --prune --no-recurse-submodules
|
||||
env:
|
||||
branch_pr: 'origin/${{ github.event.pull_request.head.ref }}'
|
||||
commits: ${{ github.event.pull_request.commits }}
|
||||
refspec_base: '+${{ github.event.pull_request.base.sha }}:remotes/origin/${{ github.event.pull_request.base.ref }}'
|
||||
refspec_pr: '+${{ github.event.pull_request.head.sha }}:remotes/origin/${{ github.event.pull_request.head.ref }}'
|
||||
|
||||
# We only want to run tests on PRs when related files are changed,
|
||||
# or when someone triggers a manual workflow run.
|
||||
- name: Compute changed files
|
||||
id: changes
|
||||
run: python Tools/build/compute-changes.py
|
||||
env:
|
||||
GITHUB_DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
|
||||
GITHUB_EVENT_NAME: ${{ github.event_name }}
|
||||
CCF_TARGET_REF: ${{ github.base_ref || github.event.repository.default_branch }}
|
||||
CCF_HEAD_REF: ${{ github.event.pull_request.head.sha || github.sha }}
|
||||
|
||||
- name: Compute hash for config cache key
|
||||
id: config-hash
|
||||
run: |
|
||||
echo "hash=${{ hashFiles('configure', 'configure.ac', '.github/workflows/build.yml') }}" >> "$GITHUB_OUTPUT"
|
||||
40
.github/workflows/reusable-docs.yml
vendored
40
.github/workflows/reusable-docs.yml
vendored
|
|
@ -15,19 +15,21 @@ env:
|
|||
FORCE_COLOR: 1
|
||||
|
||||
jobs:
|
||||
build_doc:
|
||||
build-doc:
|
||||
name: 'Docs'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
branch_base: 'origin/${{ github.event.pull_request.base.ref }}'
|
||||
branch_pr: 'origin/${{ github.event.pull_request.head.ref }}'
|
||||
commits: ${{ github.event.pull_request.commits }}
|
||||
refspec_base: '+${{ github.event.pull_request.base.sha }}:remotes/origin/${{ github.event.pull_request.base.ref }}'
|
||||
refspec_pr: '+${{ github.event.pull_request.head.sha }}:remotes/origin/${{ github.event.pull_request.head.ref }}'
|
||||
steps:
|
||||
- name: 'Check out latest PR branch commit'
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
ref: >-
|
||||
${{
|
||||
github.event_name == 'pull_request'
|
||||
|
|
@ -39,15 +41,15 @@ jobs:
|
|||
if: github.event_name == 'pull_request'
|
||||
run: |
|
||||
# Fetch enough history to find a common ancestor commit (aka merge-base):
|
||||
git fetch origin ${{ env.refspec_pr }} --depth=$(( ${{ github.event.pull_request.commits }} + 1 )) \
|
||||
git fetch origin "${refspec_pr}" --depth=$(( commits + 1 )) \
|
||||
--no-tags --prune --no-recurse-submodules
|
||||
|
||||
# This should get the oldest commit in the local fetched history (which may not be the commit the PR branched from):
|
||||
COMMON_ANCESTOR=$( git rev-list --first-parent --max-parents=0 --max-count=1 ${{ env.branch_pr }} )
|
||||
COMMON_ANCESTOR=$( git rev-list --first-parent --max-parents=0 --max-count=1 "${branch_pr}" )
|
||||
DATE=$( git log --date=iso8601 --format=%cd "${COMMON_ANCESTOR}" )
|
||||
|
||||
# Get all commits since that commit date from the base branch (eg: master or main):
|
||||
git fetch origin ${{ env.refspec_base }} --shallow-since="${DATE}" \
|
||||
git fetch origin "${refspec_base}" --shallow-since="${DATE}" \
|
||||
--no-tags --prune --no-recurse-submodules
|
||||
- name: 'Set up Python'
|
||||
uses: actions/setup-python@v5
|
||||
|
|
@ -63,42 +65,26 @@ jobs:
|
|||
continue-on-error: true
|
||||
run: |
|
||||
set -Eeuo pipefail
|
||||
# Build docs with the '-n' (nit-picky) option; write warnings to file
|
||||
make -C Doc/ PYTHON=../python SPHINXOPTS="-q -n -W --keep-going -w sphinx-warnings.txt" html
|
||||
# Build docs with the nit-picky option; write warnings to file
|
||||
make -C Doc/ PYTHON=../python SPHINXOPTS="--quiet --nitpicky --fail-on-warning --warning-file sphinx-warnings.txt" html
|
||||
- name: 'Check warnings'
|
||||
if: github.event_name == 'pull_request'
|
||||
run: |
|
||||
python Doc/tools/check-warnings.py \
|
||||
--annotate-diff '${{ env.branch_base }}' '${{ env.branch_pr }}' \
|
||||
--annotate-diff "${branch_base}" "${branch_pr}" \
|
||||
--fail-if-regression \
|
||||
--fail-if-improved \
|
||||
--fail-if-new-news-nit
|
||||
|
||||
# This build doesn't use problem matchers or check annotations
|
||||
build_doc_oldest_supported_sphinx:
|
||||
name: 'Docs (Oldest Sphinx)'
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: 'Set up Python'
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.13' # known to work with Sphinx 7.2.6
|
||||
cache: 'pip'
|
||||
cache-dependency-path: 'Doc/requirements-oldest-sphinx.txt'
|
||||
- name: 'Install build dependencies'
|
||||
run: make -C Doc/ venv REQUIREMENTS="requirements-oldest-sphinx.txt"
|
||||
- name: 'Build HTML documentation'
|
||||
run: make -C Doc/ SPHINXOPTS="-q" SPHINXERRORHANDLING="-W --keep-going" html
|
||||
|
||||
# Run "doctest" on HEAD as new syntax doesn't exist in the latest stable release
|
||||
doctest:
|
||||
name: 'Doctest'
|
||||
runs-on: ubuntu-22.04
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
|
|
@ -115,4 +101,4 @@ jobs:
|
|||
run: make -C Doc/ PYTHON=../python venv
|
||||
# Use "xvfb-run" since some doctest tests open GUI windows
|
||||
- name: 'Run documentation doctest'
|
||||
run: xvfb-run make -C Doc/ PYTHON=../python SPHINXERRORHANDLING="-W --keep-going" doctest
|
||||
run: xvfb-run make -C Doc/ PYTHON=../python SPHINXERRORHANDLING="--fail-on-warning" doctest
|
||||
|
|
|
|||
19
.github/workflows/reusable-macos.yml
vendored
19
.github/workflows/reusable-macos.yml
vendored
|
|
@ -15,9 +15,13 @@ on:
|
|||
required: true
|
||||
type: string
|
||||
|
||||
env:
|
||||
FORCE_COLOR: 1
|
||||
|
||||
jobs:
|
||||
build_macos:
|
||||
build-macos:
|
||||
name: build and test (${{ inputs.os }})
|
||||
runs-on: ${{ inputs.os }}
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
HOMEBREW_NO_ANALYTICS: 1
|
||||
|
|
@ -26,20 +30,25 @@ jobs:
|
|||
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK: 1
|
||||
PYTHONSTRICTEXTENSIONBUILD: 1
|
||||
TERM: linux
|
||||
runs-on: ${{ inputs.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Runner image version
|
||||
run: echo "IMAGE_VERSION=${ImageVersion}" >> "$GITHUB_ENV"
|
||||
run: echo "IMAGE_OS_VERSION=${ImageOS}-${ImageVersion}" >> "$GITHUB_ENV"
|
||||
- name: Restore config.cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: config.cache
|
||||
key: ${{ github.job }}-${{ inputs.os }}-${{ env.IMAGE_VERSION }}-${{ inputs.config_hash }}
|
||||
key: ${{ github.job }}-${{ env.IMAGE_OS_VERSION }}-${{ inputs.config_hash }}
|
||||
- name: Install Homebrew dependencies
|
||||
run: brew install pkg-config openssl@3.0 xz gdbm tcl-tk make
|
||||
run: |
|
||||
brew install pkg-config openssl@3.0 xz gdbm tcl-tk@8 make
|
||||
# Because alternate versions are not symlinked into place by default:
|
||||
brew link --overwrite tcl-tk@8
|
||||
- name: Configure CPython
|
||||
run: |
|
||||
MACOSX_DEPLOYMENT_TARGET=10.15 \
|
||||
GDBM_CFLAGS="-I$(brew --prefix gdbm)/include" \
|
||||
GDBM_LIBS="-L$(brew --prefix gdbm)/lib -lgdbm" \
|
||||
./configure \
|
||||
|
|
|
|||
54
.github/workflows/reusable-tsan.yml
vendored
54
.github/workflows/reusable-tsan.yml
vendored
|
|
@ -6,33 +6,32 @@ on:
|
|||
config_hash:
|
||||
required: true
|
||||
type: string
|
||||
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
|
||||
free-threading:
|
||||
description: Whether to use free-threaded mode
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
|
||||
env:
|
||||
FORCE_COLOR: 1
|
||||
|
||||
jobs:
|
||||
build_tsan_reusable:
|
||||
build-tsan-reusable:
|
||||
name: 'Thread sanitizer'
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 60
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Runner image version
|
||||
run: echo "IMAGE_VERSION=${ImageVersion}" >> "$GITHUB_ENV"
|
||||
run: echo "IMAGE_OS_VERSION=${ImageOS}-${ImageVersion}" >> "$GITHUB_ENV"
|
||||
- name: Restore config.cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: config.cache
|
||||
key: ${{ github.job }}-${{ runner.os }}-${{ env.IMAGE_VERSION }}-${{ inputs.config_hash }}
|
||||
- name: Install Dependencies
|
||||
key: ${{ github.job }}-${{ env.IMAGE_OS_VERSION }}-${{ inputs.config_hash }}
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
sudo ./.github/workflows/posix-deps-apt.sh
|
||||
# Install clang-18
|
||||
|
|
@ -45,9 +44,13 @@ jobs:
|
|||
sudo update-alternatives --set clang++ /usr/bin/clang++-17
|
||||
# Reduce ASLR to avoid TSAN crashing
|
||||
sudo sysctl -w vm.mmap_rnd_bits=28
|
||||
- name: TSAN Option Setup
|
||||
- name: TSAN option setup
|
||||
run: |
|
||||
echo "TSAN_OPTIONS=log_path=${GITHUB_WORKSPACE}/tsan_log suppressions=${GITHUB_WORKSPACE}/${{ inputs.suppressions_path }} handle_segv=0" >> "$GITHUB_ENV"
|
||||
echo "TSAN_OPTIONS=log_path=${GITHUB_WORKSPACE}/tsan_log suppressions=${GITHUB_WORKSPACE}/Tools/tsan/suppressions${{
|
||||
fromJSON(inputs.free-threading)
|
||||
&& '_free_threading'
|
||||
|| ''
|
||||
}}.txt handle_segv=0" >> "$GITHUB_ENV"
|
||||
echo "CC=clang" >> "$GITHUB_ENV"
|
||||
echo "CXX=clang++" >> "$GITHUB_ENV"
|
||||
- name: Add ccache to PATH
|
||||
|
|
@ -59,13 +62,21 @@ jobs:
|
|||
save: ${{ github.event_name == 'push' }}
|
||||
max-size: "200M"
|
||||
- name: Configure CPython
|
||||
run: ${{ inputs.options }}
|
||||
run: >-
|
||||
./configure
|
||||
--config-cache
|
||||
--with-thread-sanitizer
|
||||
--with-pydebug
|
||||
${{ fromJSON(inputs.free-threading) && '--disable-gil' || '' }}
|
||||
- name: Build CPython
|
||||
run: make -j4
|
||||
- name: Display build info
|
||||
run: make pythoninfo
|
||||
- name: Tests
|
||||
run: ./python -m test --tsan -j4
|
||||
- name: Parallel tests
|
||||
if: fromJSON(inputs.free-threading)
|
||||
run: ./python -m test --tsan-parallel --parallel-threads=4 -j4
|
||||
- name: Display TSAN logs
|
||||
if: always()
|
||||
run: find "${GITHUB_WORKSPACE}" -name 'tsan_log.*' | xargs head -n 1000
|
||||
|
|
@ -73,6 +84,11 @@ jobs:
|
|||
if: always()
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: ${{ inputs.tsan_logs_artifact_name }}
|
||||
name: >-
|
||||
tsan-logs-${{
|
||||
fromJSON(inputs.free-threading)
|
||||
&& 'free-threading'
|
||||
|| 'default'
|
||||
}}
|
||||
path: tsan_log.*
|
||||
if-no-files-found: ignore
|
||||
|
|
|
|||
45
.github/workflows/reusable-ubuntu.yml
vendored
45
.github/workflows/reusable-ubuntu.yml
vendored
|
|
@ -6,32 +6,47 @@ on:
|
|||
config_hash:
|
||||
required: true
|
||||
type: string
|
||||
bolt-optimizations:
|
||||
description: Whether to enable BOLT optimizations
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
free-threading:
|
||||
description: Whether to use free-threaded mode
|
||||
required: false
|
||||
type: boolean
|
||||
default: false
|
||||
os:
|
||||
description: OS to run the job
|
||||
required: true
|
||||
type: string
|
||||
|
||||
env:
|
||||
FORCE_COLOR: 1
|
||||
|
||||
jobs:
|
||||
build_ubuntu_reusable:
|
||||
name: 'build and test'
|
||||
build-ubuntu-reusable:
|
||||
name: build and test (${{ inputs.os }})
|
||||
runs-on: ${{ inputs.os }}
|
||||
timeout-minutes: 60
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-24.04, ubuntu-24.04-aarch64]
|
||||
env:
|
||||
FORCE_COLOR: 1
|
||||
OPENSSL_VER: 3.0.15
|
||||
PYTHONSTRICTEXTENSIONBUILD: 1
|
||||
TERM: linux
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Register gcc problem matcher
|
||||
run: echo "::add-matcher::.github/problem-matchers/gcc.json"
|
||||
- name: Install dependencies
|
||||
run: sudo ./.github/workflows/posix-deps-apt.sh
|
||||
- name: Install Clang and BOLT
|
||||
if: ${{ fromJSON(inputs.bolt-optimizations) }}
|
||||
run: |
|
||||
sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" ./llvm.sh 19
|
||||
sudo apt-get install bolt-19
|
||||
echo PATH="$(llvm-config-19 --bindir):$PATH" >> $GITHUB_ENV
|
||||
- name: Configure OpenSSL env vars
|
||||
run: |
|
||||
echo "MULTISSL_DIR=${GITHUB_WORKSPACE}/multissl" >> "$GITHUB_ENV"
|
||||
|
|
@ -42,7 +57,7 @@ jobs:
|
|||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ./multissl/openssl/${{ env.OPENSSL_VER }}
|
||||
key: ${{ matrix.os }}-multissl-openssl-${{ env.OPENSSL_VER }}
|
||||
key: ${{ inputs.os }}-multissl-openssl-${{ env.OPENSSL_VER }}
|
||||
- name: Install OpenSSL
|
||||
if: steps.cache-openssl.outputs.cache-hit != 'true'
|
||||
run: python3 Tools/ssl/multissltests.py --steps=library --base-directory "$MULTISSL_DIR" --openssl "$OPENSSL_VER" --system Linux
|
||||
|
|
@ -63,15 +78,18 @@ jobs:
|
|||
- name: Bind mount sources read-only
|
||||
run: sudo mount --bind -o ro "$GITHUB_WORKSPACE" "$CPYTHON_RO_SRCDIR"
|
||||
- name: Runner image version
|
||||
run: echo "IMAGE_VERSION=${ImageVersion}" >> "$GITHUB_ENV"
|
||||
run: echo "IMAGE_OS_VERSION=${ImageOS}-${ImageVersion}" >> "$GITHUB_ENV"
|
||||
- name: Restore config.cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: ${{ env.CPYTHON_BUILDDIR }}/config.cache
|
||||
key: ${{ github.job }}-${{ runner.os }}-${{ env.IMAGE_VERSION }}-${{ inputs.config_hash }}
|
||||
key: ${{ github.job }}-${{ env.IMAGE_OS_VERSION }}-${{ inputs.config_hash }}
|
||||
- name: Configure CPython out-of-tree
|
||||
working-directory: ${{ env.CPYTHON_BUILDDIR }}
|
||||
# `test_unpickle_module_race` writes to the source directory, which is
|
||||
# read-only during builds — so we exclude it from profiling with BOLT.
|
||||
run: >-
|
||||
PROFILE_TASK='-m test --pgo --ignore test_unpickle_module_race'
|
||||
../cpython-ro-srcdir/configure
|
||||
--config-cache
|
||||
--with-pydebug
|
||||
|
|
@ -79,12 +97,13 @@ jobs:
|
|||
--enable-safety
|
||||
--with-openssl="$OPENSSL_DIR"
|
||||
${{ fromJSON(inputs.free-threading) && '--disable-gil' || '' }}
|
||||
${{ fromJSON(inputs.bolt-optimizations) && '--enable-bolt' || '' }}
|
||||
- name: Build CPython out-of-tree
|
||||
if: ${{ inputs.free-threading }}
|
||||
working-directory: ${{ env.CPYTHON_BUILDDIR }}
|
||||
run: make -j
|
||||
- name: Build CPython out-of-tree (for compiler warning check)
|
||||
if: ${{ !inputs.free-threading}}
|
||||
if: ${{ !inputs.free-threading }}
|
||||
working-directory: ${{ env.CPYTHON_BUILDDIR }}
|
||||
run: set -o pipefail; make -j --output-sync 2>&1 | tee compiler_output_ubuntu.txt
|
||||
- name: Display build info
|
||||
|
|
@ -94,7 +113,7 @@ jobs:
|
|||
if: ${{ !inputs.free-threading }}
|
||||
run: >-
|
||||
python Tools/build/check_warnings.py
|
||||
--compiler-output-file-path=${{ env.CPYTHON_BUILDDIR }}/compiler_output_ubuntu.txt
|
||||
--compiler-output-file-path="${CPYTHON_BUILDDIR}/compiler_output_ubuntu.txt"
|
||||
--warning-ignore-file-path "${GITHUB_WORKSPACE}/Tools/build/.warningignore_ubuntu"
|
||||
--compiler-output-type=gcc
|
||||
--fail-on-regression
|
||||
|
|
|
|||
25
.github/workflows/reusable-wasi.yml
vendored
25
.github/workflows/reusable-wasi.yml
vendored
|
|
@ -7,11 +7,14 @@ on:
|
|||
required: true
|
||||
type: string
|
||||
|
||||
env:
|
||||
FORCE_COLOR: 1
|
||||
|
||||
jobs:
|
||||
build_wasi_reusable:
|
||||
build-wasi-reusable:
|
||||
name: 'build and test'
|
||||
timeout-minutes: 60
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
WASMTIME_VERSION: 22.0.0
|
||||
WASI_SDK_VERSION: 24
|
||||
|
|
@ -20,6 +23,8 @@ jobs:
|
|||
CROSS_BUILD_WASI: cross-build/wasm32-wasip1
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
# No problem resolver registered as one doesn't currently exist for Clang.
|
||||
- name: "Install wasmtime"
|
||||
uses: bytecodealliance/actions/wasmtime/setup@v1
|
||||
|
|
@ -34,9 +39,9 @@ jobs:
|
|||
- name: "Install WASI SDK" # Hard-coded to x64.
|
||||
if: steps.cache-wasi-sdk.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
mkdir ${{ env.WASI_SDK_PATH }} && \
|
||||
curl -s -S --location https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${{ env.WASI_SDK_VERSION }}/wasi-sdk-${{ env.WASI_SDK_VERSION }}.0-x86_64-linux.tar.gz | \
|
||||
tar --strip-components 1 --directory ${{ env.WASI_SDK_PATH }} --extract --gunzip
|
||||
mkdir "${WASI_SDK_PATH}" && \
|
||||
curl -s -S --location "https://github.com/WebAssembly/wasi-sdk/releases/download/wasi-sdk-${WASI_SDK_VERSION}/wasi-sdk-${WASI_SDK_VERSION}.0-x86_64-linux.tar.gz" | \
|
||||
tar --strip-components 1 --directory "${WASI_SDK_PATH}" --extract --gunzip
|
||||
- name: "Configure ccache action"
|
||||
uses: hendrikmuhs/ccache-action@v1.2
|
||||
with:
|
||||
|
|
@ -48,6 +53,8 @@ jobs:
|
|||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.x'
|
||||
- name: "Runner image version"
|
||||
run: echo "IMAGE_OS_VERSION=${ImageOS}-${ImageVersion}" >> "$GITHUB_ENV"
|
||||
- name: "Restore Python build config.cache"
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
|
|
@ -55,7 +62,7 @@ jobs:
|
|||
# Include env.pythonLocation in key to avoid changes in environment when setup-python updates Python.
|
||||
# Include the hash of `Tools/wasm/wasi.py` as it may change the environment variables.
|
||||
# (Make sure to keep the key in sync with the other config.cache step below.)
|
||||
key: ${{ github.job }}-${{ runner.os }}-${{ env.IMAGE_VERSION }}-${{ env.WASI_SDK_VERSION }}-${{ env.WASMTIME_VERSION }}-${{ inputs.config_hash }}-${{ hashFiles('Tools/wasm/wasi.py') }}-${{ env.pythonLocation }}
|
||||
key: ${{ github.job }}-${{ env.IMAGE_OS_VERSION }}-${{ env.WASI_SDK_VERSION }}-${{ env.WASMTIME_VERSION }}-${{ inputs.config_hash }}-${{ hashFiles('Tools/wasm/wasi.py') }}-${{ env.pythonLocation }}
|
||||
- name: "Configure build Python"
|
||||
run: python3 Tools/wasm/wasi.py configure-build-python -- --config-cache --with-pydebug
|
||||
- name: "Make build Python"
|
||||
|
|
@ -65,13 +72,13 @@ jobs:
|
|||
with:
|
||||
path: ${{ env.CROSS_BUILD_WASI }}/config.cache
|
||||
# Should be kept in sync with the other config.cache step above.
|
||||
key: ${{ github.job }}-${{ runner.os }}-${{ env.IMAGE_VERSION }}-${{ env.WASI_SDK_VERSION }}-${{ env.WASMTIME_VERSION }}-${{ inputs.config_hash }}-${{ hashFiles('Tools/wasm/wasi.py') }}-${{ env.pythonLocation }}
|
||||
key: ${{ github.job }}-${{ env.IMAGE_OS_VERSION }}-${{ env.WASI_SDK_VERSION }}-${{ env.WASMTIME_VERSION }}-${{ inputs.config_hash }}-${{ hashFiles('Tools/wasm/wasi.py') }}-${{ env.pythonLocation }}
|
||||
- name: "Configure host"
|
||||
# `--with-pydebug` inferred from configure-build-python
|
||||
run: python3 Tools/wasm/wasi.py configure-host -- --config-cache
|
||||
- name: "Make host"
|
||||
run: python3 Tools/wasm/wasi.py make-host
|
||||
- name: "Display build info"
|
||||
run: make --directory ${{ env.CROSS_BUILD_WASI }} pythoninfo
|
||||
run: make --directory "${CROSS_BUILD_WASI}" pythoninfo
|
||||
- name: "Test"
|
||||
run: make --directory ${{ env.CROSS_BUILD_WASI }} test
|
||||
run: make --directory "${CROSS_BUILD_WASI}" test
|
||||
|
|
|
|||
11
.github/workflows/reusable-windows-msi.yml
vendored
11
.github/workflows/reusable-windows-msi.yml
vendored
|
|
@ -11,14 +11,21 @@ on:
|
|||
permissions:
|
||||
contents: read
|
||||
|
||||
env:
|
||||
FORCE_COLOR: 1
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: installer for ${{ inputs.arch }}
|
||||
runs-on: windows-latest
|
||||
runs-on: ${{ inputs.arch == 'arm64' && 'windows-11-arm' || 'windows-latest' }}
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
ARCH: ${{ inputs.arch }}
|
||||
IncludeFreethreaded: true
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Build CPython installer
|
||||
run: .\Tools\msi\build.bat --doc -${{ inputs.arch }}
|
||||
run: ./Tools/msi/build.bat --doc -"${ARCH}"
|
||||
shell: bash
|
||||
|
|
|
|||
25
.github/workflows/reusable-windows.yml
vendored
25
.github/workflows/reusable-windows.yml
vendored
|
|
@ -3,10 +3,6 @@ name: Reusable Windows
|
|||
on:
|
||||
workflow_call:
|
||||
inputs:
|
||||
os:
|
||||
description: OS to run on
|
||||
required: true
|
||||
type: string
|
||||
arch:
|
||||
description: CPU architecture
|
||||
required: true
|
||||
|
|
@ -18,16 +14,21 @@ on:
|
|||
default: false
|
||||
|
||||
env:
|
||||
FORCE_COLOR: 1
|
||||
IncludeUwp: >-
|
||||
true
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: 'build and test (${{ inputs.arch }})'
|
||||
runs-on: ${{ inputs.os }}
|
||||
name: Build and test (${{ inputs.arch }})
|
||||
runs-on: ${{ inputs.arch == 'arm64' && 'windows-11-arm' || 'windows-latest' }}
|
||||
timeout-minutes: 60
|
||||
env:
|
||||
ARCH: ${{ inputs.arch }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- name: Register MSVC problem matcher
|
||||
if: inputs.arch != 'Win32'
|
||||
run: echo "::add-matcher::.github/problem-matchers/msvc.json"
|
||||
|
|
@ -35,15 +36,15 @@ jobs:
|
|||
run: >-
|
||||
.\\PCbuild\\build.bat
|
||||
-e -d -v
|
||||
-p ${{ inputs.arch }}
|
||||
-p "${ARCH}"
|
||||
${{ fromJSON(inputs.free-threading) && '--disable-gil' || '' }}
|
||||
- name: Display build info # FIXME(diegorusso): remove the `if`
|
||||
if: inputs.arch != 'arm64'
|
||||
shell: bash
|
||||
- name: Display build info
|
||||
run: .\\python.bat -m test.pythoninfo
|
||||
- name: Tests # FIXME(diegorusso): remove the `if`
|
||||
if: inputs.arch != 'arm64'
|
||||
- name: Tests
|
||||
run: >-
|
||||
.\\PCbuild\\rt.bat
|
||||
-p ${{ inputs.arch }}
|
||||
-p "${ARCH}"
|
||||
-d -q --fast-ci
|
||||
${{ fromJSON(inputs.free-threading) && '--disable-gil' || '' }}
|
||||
shell: bash
|
||||
|
|
|
|||
6
.github/workflows/stale.yml
vendored
6
.github/workflows/stale.yml
vendored
|
|
@ -4,14 +4,12 @@ on:
|
|||
schedule:
|
||||
- cron: "0 */6 * * *"
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
if: github.repository_owner == 'python'
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pull-requests: write
|
||||
timeout-minutes: 10
|
||||
|
||||
steps:
|
||||
|
|
|
|||
140
.github/workflows/tail-call.yml
vendored
Normal file
140
.github/workflows/tail-call.yml
vendored
Normal file
|
|
@ -0,0 +1,140 @@
|
|||
name: Tail calling interpreter
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- '.github/workflows/tail-call.yml'
|
||||
- 'Python/bytecodes.c'
|
||||
- 'Python/ceval.c'
|
||||
- 'Python/ceval_macros.h'
|
||||
- 'Python/generated_cases.c.h'
|
||||
push:
|
||||
paths:
|
||||
- '.github/workflows/tail-call.yml'
|
||||
- 'Python/bytecodes.c'
|
||||
- 'Python/ceval.c'
|
||||
- 'Python/ceval_macros.h'
|
||||
- 'Python/generated_cases.c.h'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
FORCE_COLOR: 1
|
||||
|
||||
jobs:
|
||||
tail-call:
|
||||
name: ${{ matrix.target }}
|
||||
runs-on: ${{ matrix.runner }}
|
||||
timeout-minutes: 90
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
target:
|
||||
# Un-comment as we add support for more platforms for tail-calling interpreters.
|
||||
# - i686-pc-windows-msvc/msvc
|
||||
- x86_64-pc-windows-msvc/msvc
|
||||
# - aarch64-pc-windows-msvc/msvc
|
||||
- x86_64-apple-darwin/clang
|
||||
- aarch64-apple-darwin/clang
|
||||
- x86_64-unknown-linux-gnu/gcc
|
||||
- aarch64-unknown-linux-gnu/gcc
|
||||
- free-threading
|
||||
llvm:
|
||||
- 20
|
||||
include:
|
||||
# - target: i686-pc-windows-msvc/msvc
|
||||
# architecture: Win32
|
||||
# runner: windows-latest
|
||||
- target: x86_64-pc-windows-msvc/msvc
|
||||
architecture: x64
|
||||
runner: windows-latest
|
||||
# - target: aarch64-pc-windows-msvc/msvc
|
||||
# architecture: ARM64
|
||||
# runner: windows-latest
|
||||
- target: x86_64-apple-darwin/clang
|
||||
architecture: x86_64
|
||||
runner: macos-13
|
||||
- target: aarch64-apple-darwin/clang
|
||||
architecture: aarch64
|
||||
runner: macos-14
|
||||
- target: x86_64-unknown-linux-gnu/gcc
|
||||
architecture: x86_64
|
||||
runner: ubuntu-24.04
|
||||
- target: aarch64-unknown-linux-gnu/gcc
|
||||
architecture: aarch64
|
||||
runner: ubuntu-24.04-arm
|
||||
- target: free-threading
|
||||
architecture: x86_64
|
||||
runner: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Native Windows (debug)
|
||||
if: runner.os == 'Windows' && matrix.architecture != 'ARM64'
|
||||
shell: cmd
|
||||
run: |
|
||||
choco install llvm --allow-downgrade --no-progress --version ${{ matrix.llvm }}.1.0
|
||||
set PlatformToolset=clangcl
|
||||
set LLVMToolsVersion=${{ matrix.llvm }}.1.0
|
||||
set LLVMInstallDir=C:\Program Files\LLVM
|
||||
call ./PCbuild/build.bat --tail-call-interp -d -p ${{ matrix.architecture }}
|
||||
call ./PCbuild/rt.bat -d -p ${{ matrix.architecture }} -q --multiprocess 0 --timeout 4500 --verbose2 --verbose3
|
||||
|
||||
# No tests (yet):
|
||||
- name: Emulated Windows (release)
|
||||
if: runner.os == 'Windows' && matrix.architecture == 'ARM64'
|
||||
shell: cmd
|
||||
run: |
|
||||
choco install llvm --allow-downgrade --no-progress --version ${{ matrix.llvm }}.1.0
|
||||
set PlatformToolset=clangcl
|
||||
set LLVMToolsVersion=${{ matrix.llvm }}.1.0
|
||||
set LLVMInstallDir=C:\Program Files\LLVM
|
||||
./PCbuild/build.bat --tail-call-interp -p ${{ matrix.architecture }}
|
||||
|
||||
# The `find` line is required as a result of https://github.com/actions/runner-images/issues/9966.
|
||||
# This is a bug in the macOS runner image where the pre-installed Python is installed in the same
|
||||
# directory as the Homebrew Python, which causes the build to fail for macos-13. This line removes
|
||||
# the symlink to the pre-installed Python so that the Homebrew Python is used instead.
|
||||
# Note: when a new LLVM is released, the homebrew installation directory changes, so the builds will fail.
|
||||
# We either need to upgrade LLVM or change the directory being pointed to.
|
||||
- name: Native macOS (release)
|
||||
if: runner.os == 'macOS'
|
||||
run: |
|
||||
brew update
|
||||
find /usr/local/bin -lname '*/Library/Frameworks/Python.framework/*' -delete
|
||||
brew install llvm@${{ matrix.llvm }}
|
||||
export SDKROOT="$(xcrun --show-sdk-path)"
|
||||
export PATH="/usr/local/opt/llvm/bin:$PATH"
|
||||
export PATH="/opt/homebrew/opt/llvm/bin:$PATH"
|
||||
CC=clang-20 ./configure --with-tail-call-interp
|
||||
make all --jobs 4
|
||||
./python.exe -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3
|
||||
|
||||
- name: Native Linux (debug)
|
||||
if: runner.os == 'Linux' && matrix.target != 'free-threading'
|
||||
run: |
|
||||
sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" ./llvm.sh ${{ matrix.llvm }}
|
||||
export PATH="$(llvm-config-${{ matrix.llvm }} --bindir):$PATH"
|
||||
CC=clang-20 ./configure --with-tail-call-interp --with-pydebug
|
||||
make all --jobs 4
|
||||
./python -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3
|
||||
|
||||
- name: Native Linux with free-threading (release)
|
||||
if: matrix.target == 'free-threading'
|
||||
run: |
|
||||
sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" ./llvm.sh ${{ matrix.llvm }}
|
||||
export PATH="$(llvm-config-${{ matrix.llvm }} --bindir):$PATH"
|
||||
CC=clang-20 ./configure --with-tail-call-interp --disable-gil
|
||||
make all --jobs 4
|
||||
./python -m test --multiprocess 0 --timeout 4500 --verbose2 --verbose3
|
||||
|
||||
|
|
@ -26,6 +26,8 @@ jobs:
|
|||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
persist-credentials: false
|
||||
- uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3'
|
||||
|
|
|
|||
10
.github/zizmor.yml
vendored
Normal file
10
.github/zizmor.yml
vendored
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# Configuration for the zizmor static analysis tool, run via pre-commit in CI
|
||||
# https://woodruffw.github.io/zizmor/configuration/
|
||||
rules:
|
||||
dangerous-triggers:
|
||||
ignore:
|
||||
- documentation-links.yml
|
||||
unpinned-uses:
|
||||
config:
|
||||
policies:
|
||||
"*": ref-pin
|
||||
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -38,6 +38,7 @@ tags
|
|||
TAGS
|
||||
.vs/
|
||||
.vscode/
|
||||
.cache/
|
||||
gmon.out
|
||||
.coverage
|
||||
.mypy_cache/
|
||||
|
|
@ -137,11 +138,12 @@ Tools/unicode/data/
|
|||
# hendrikmuhs/ccache-action@v1
|
||||
/.ccache
|
||||
/cross-build/
|
||||
/jit_stencils.h
|
||||
/jit_stencils*.h
|
||||
/platform
|
||||
/profile-clean-stamp
|
||||
/profile-run-stamp
|
||||
/profile-bolt-stamp
|
||||
/profile-gen-stamp
|
||||
/pybuilddir.txt
|
||||
/pyconfig.h
|
||||
/python-config
|
||||
|
|
|
|||
1
.mailmap
1
.mailmap
|
|
@ -1,3 +1,4 @@
|
|||
# This file sets the canonical name for contributors to the repository.
|
||||
# Documentation: https://git-scm.com/docs/gitmailmap
|
||||
Willow Chargin <wchargin@gmail.com>
|
||||
Amethyst Reese <amethyst@n7.gg> <john@noswap.com>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
repos:
|
||||
- repo: https://github.com/astral-sh/ruff-pre-commit
|
||||
rev: v0.7.1
|
||||
rev: v0.11.8
|
||||
hooks:
|
||||
- id: ruff
|
||||
name: Run Ruff (lint) on Doc/
|
||||
|
|
@ -11,9 +11,9 @@ repos:
|
|||
args: [--exit-non-zero-on-fix]
|
||||
files: ^Lib/test/
|
||||
- id: ruff
|
||||
name: Run Ruff (lint) on Tools/build/check_warnings.py
|
||||
name: Run Ruff (lint) on Tools/build/
|
||||
args: [--exit-non-zero-on-fix, --config=Tools/build/.ruff.toml]
|
||||
files: ^Tools/build/check_warnings.py
|
||||
files: ^Tools/build/
|
||||
- id: ruff
|
||||
name: Run Ruff (lint) on Argument Clinic
|
||||
args: [--exit-non-zero-on-fix, --config=Tools/clinic/.ruff.toml]
|
||||
|
|
@ -22,19 +22,17 @@ repos:
|
|||
name: Run Ruff (format) on Doc/
|
||||
args: [--check]
|
||||
files: ^Doc/
|
||||
- id: ruff-format
|
||||
name: Run Ruff (format) on Tools/build/check_warnings.py
|
||||
args: [--check, --config=Tools/build/.ruff.toml]
|
||||
files: ^Tools/build/check_warnings.py
|
||||
|
||||
- repo: https://github.com/psf/black-pre-commit-mirror
|
||||
rev: 24.10.0
|
||||
rev: 25.1.0
|
||||
hooks:
|
||||
- id: black
|
||||
name: Run Black on Tools/build/check_warnings.py
|
||||
files: ^Tools/build/check_warnings.py
|
||||
language_version: python3.12
|
||||
args: [--line-length=79]
|
||||
- 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: v5.0.0
|
||||
|
|
@ -49,18 +47,26 @@ repos:
|
|||
exclude: Lib/test/tokenizedata/coding20731.py
|
||||
- id: trailing-whitespace
|
||||
types_or: [c, inc, python, rst]
|
||||
- id: trailing-whitespace
|
||||
files: '\.(gram)$'
|
||||
|
||||
- repo: https://github.com/python-jsonschema/check-jsonschema
|
||||
rev: 0.29.4
|
||||
rev: 0.33.0
|
||||
hooks:
|
||||
- id: check-dependabot
|
||||
- id: check-github-workflows
|
||||
- id: check-readthedocs
|
||||
|
||||
- repo: https://github.com/rhysd/actionlint
|
||||
rev: v1.7.4
|
||||
rev: v1.7.7
|
||||
hooks:
|
||||
- id: actionlint
|
||||
|
||||
- repo: https://github.com/woodruffw/zizmor-pre-commit
|
||||
rev: v1.6.0
|
||||
hooks:
|
||||
- id: zizmor
|
||||
|
||||
- repo: https://github.com/sphinx-contrib/sphinx-lint
|
||||
rev: v1.0.0
|
||||
hooks:
|
||||
|
|
|
|||
12
.ruff.toml
Normal file
12
.ruff.toml
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# Default settings for Ruff in CPython
|
||||
|
||||
# PYTHON_FOR_REGEN
|
||||
target-version = "py310"
|
||||
|
||||
# PEP 8
|
||||
line-length = 79
|
||||
|
||||
# Enable automatic fixes by default.
|
||||
# To override this, use ``fix = false`` in a subdirectory's config file
|
||||
# or ``--no-fix`` on the command line.
|
||||
fix = true
|
||||
|
|
@ -1,19 +1,22 @@
|
|||
# Python for Android
|
||||
|
||||
These instructions are only needed if you're planning to compile Python for
|
||||
Android yourself. Most users should *not* need to do this. Instead, use one of
|
||||
the tools listed in `Doc/using/android.rst`, which will provide a much easier
|
||||
experience.
|
||||
If you obtained this README as part of a release package, then the only
|
||||
applicable sections are "Prerequisites", "Testing", and "Using in your own app".
|
||||
|
||||
If you obtained this README as part of the CPython source tree, then you can
|
||||
also follow the other sections to compile Python for Android yourself.
|
||||
|
||||
However, most app developers should not need to do any of these things manually.
|
||||
Instead, use one of the tools listed
|
||||
[here](https://docs.python.org/3/using/android.html), which will provide a much
|
||||
easier experience.
|
||||
|
||||
|
||||
## Prerequisites
|
||||
|
||||
First, make sure you have all the usual tools and libraries needed to build
|
||||
Python for your development machine.
|
||||
|
||||
Second, you'll need an Android SDK. If you already have the SDK installed,
|
||||
export the `ANDROID_HOME` environment variable to point at its location.
|
||||
Otherwise, here's how to install it:
|
||||
If you already have an Android SDK installed, export the `ANDROID_HOME`
|
||||
environment variable to point at its location. Otherwise, here's how to install
|
||||
it:
|
||||
|
||||
* Download the "Command line tools" from <https://developer.android.com/studio>.
|
||||
* Create a directory `android-sdk/cmdline-tools`, and unzip the command line
|
||||
|
|
@ -22,20 +25,23 @@ Otherwise, 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`:
|
||||
The `android.py` script will automatically use the SDK's `sdkmanager` to install
|
||||
any packages it needs.
|
||||
|
||||
The script also requires the following commands to be on the `PATH`:
|
||||
|
||||
* `curl`
|
||||
* `java` (or set the `JAVA_HOME` environment variable)
|
||||
* `tar`
|
||||
* `unzip`
|
||||
|
||||
|
||||
## Building
|
||||
|
||||
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.
|
||||
development tools, which currently means Linux or macOS.
|
||||
|
||||
First we'll make a "build" Python (for your development machine), then use it to
|
||||
help produce a "host" Python for Android. So make sure you have all the usual
|
||||
tools and libraries needed to build Python for your development machine.
|
||||
|
||||
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
|
||||
|
|
@ -60,8 +66,8 @@ To do all steps in a single command, run:
|
|||
./android.py build HOST
|
||||
```
|
||||
|
||||
In the end you should have a build Python in `cross-build/build`, and an Android
|
||||
build in `cross-build/HOST`.
|
||||
In the end you should have a build Python in `cross-build/build`, and a host
|
||||
Python in `cross-build/HOST`.
|
||||
|
||||
You can use `--` as a separator for any of the `configure`-related commands –
|
||||
including `build` itself – to pass arguments to the underlying `configure`
|
||||
|
|
@ -73,14 +79,27 @@ call. For example, if you want a pydebug build that also caches the results from
|
|||
```
|
||||
|
||||
|
||||
## Packaging
|
||||
|
||||
After building an architecture as described in the section above, you can
|
||||
package it for release with this command:
|
||||
|
||||
```sh
|
||||
./android.py package HOST
|
||||
```
|
||||
|
||||
`HOST` is defined in the section above.
|
||||
|
||||
This will generate a tarball in `cross-build/HOST/dist`, whose structure is
|
||||
similar to the `Android` directory of the CPython source tree.
|
||||
|
||||
|
||||
## Testing
|
||||
|
||||
The test suite can be run on Linux, macOS, or Windows:
|
||||
The Python test suite can be run on Linux, macOS, or Windows:
|
||||
|
||||
* On Linux, the emulator needs access to the KVM virtualization interface, and
|
||||
a DISPLAY environment variable pointing at an X server.
|
||||
* On Windows, you won't be able to do the build on the same machine, so you'll
|
||||
have to copy the `cross-build/HOST` directory from somewhere else.
|
||||
a DISPLAY environment variable pointing at an X server. Xvfb is acceptable.
|
||||
|
||||
The test suite can usually be run on a device with 2 GB of RAM, but this is
|
||||
borderline, so you may need to increase it to 4 GB. As of Android
|
||||
|
|
@ -90,9 +109,16 @@ and find `hw.ramSize` in both config.ini and hardware-qemu.ini. Either set these
|
|||
manually to the same value, or use the Android Studio Device Manager, which will
|
||||
update both files.
|
||||
|
||||
Before running the test suite, follow the instructions in the previous section
|
||||
to build the architecture you want to test. Then run the test script in one of
|
||||
the following modes:
|
||||
You can run the test suite either:
|
||||
|
||||
* Within the CPython repository, after doing a build as described above. On
|
||||
Windows, you won't be able to do the build on the same machine, so you'll have
|
||||
to copy the `cross-build/HOST/prefix` directory from somewhere else.
|
||||
|
||||
* Or by taking a release package built using the `package` command, extracting
|
||||
it wherever you want, and using its own copy of `android.py`.
|
||||
|
||||
The test script supports the following modes:
|
||||
|
||||
* In `--connected` mode, it runs on a device or emulator you have already
|
||||
connected to the build machine. List the available devices with
|
||||
|
|
@ -119,10 +145,10 @@ stderr. Add the `-v` option to also show Gradle output, and non-Python logcat
|
|||
messages.
|
||||
|
||||
Any other arguments on the `android.py test` command line will be passed through
|
||||
to `python -m test` – use `--` to separate them from android.py's own options.
|
||||
to `python -m test` – use `--` to separate them from android.py's own options.
|
||||
See the [Python Developer's
|
||||
Guide](https://devguide.python.org/testing/run-write-tests/) for common options
|
||||
– most of them will work on Android, except for those that involve subprocesses,
|
||||
– most of them will work on Android, except for those that involve subprocesses,
|
||||
such as `-j`.
|
||||
|
||||
Every time you run `android.py test`, changes in pure-Python files in the
|
||||
|
|
@ -133,4 +159,4 @@ until you re-run `android.py make-host` or `build`.
|
|||
|
||||
## Using in your own app
|
||||
|
||||
See `Doc/using/android.rst`.
|
||||
See https://docs.python.org/3/using/android.html.
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
# This script must be sourced with the following variables already set:
|
||||
: ${ANDROID_HOME:?} # Path to Android SDK
|
||||
: ${HOST:?} # GNU target triplet
|
||||
: "${ANDROID_HOME:?}" # Path to Android SDK
|
||||
: "${HOST:?}" # GNU target triplet
|
||||
|
||||
# You may also override the following:
|
||||
: ${api_level:=24} # Minimum Android API level the build will run on
|
||||
: ${PREFIX:-} # Path in which to find required libraries
|
||||
: "${api_level:=24}" # Minimum Android API level the build will run on
|
||||
: "${PREFIX:-}" # Path in which to find required libraries
|
||||
|
||||
|
||||
# Print all messages on stderr so they're visible when running within build-wheel.
|
||||
|
|
@ -27,20 +27,20 @@ fail() {
|
|||
ndk_version=27.1.12297006
|
||||
|
||||
ndk=$ANDROID_HOME/ndk/$ndk_version
|
||||
if ! [ -e $ndk ]; then
|
||||
if ! [ -e "$ndk" ]; then
|
||||
log "Installing NDK - this may take several minutes"
|
||||
yes | $ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager "ndk;$ndk_version"
|
||||
yes | "$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" "ndk;$ndk_version"
|
||||
fi
|
||||
|
||||
if [ $HOST = "arm-linux-androideabi" ]; then
|
||||
if [ "$HOST" = "arm-linux-androideabi" ]; then
|
||||
clang_triplet=armv7a-linux-androideabi
|
||||
else
|
||||
clang_triplet=$HOST
|
||||
clang_triplet="$HOST"
|
||||
fi
|
||||
|
||||
# These variables are based on BuildSystemMaintainers.md above, and
|
||||
# $ndk/build/cmake/android.toolchain.cmake.
|
||||
toolchain=$(echo $ndk/toolchains/llvm/prebuilt/*)
|
||||
toolchain=$(echo "$ndk"/toolchains/llvm/prebuilt/*)
|
||||
export AR="$toolchain/bin/llvm-ar"
|
||||
export AS="$toolchain/bin/llvm-as"
|
||||
export CC="$toolchain/bin/${clang_triplet}${api_level}-clang"
|
||||
|
|
@ -72,12 +72,12 @@ LDFLAGS="$LDFLAGS -lm"
|
|||
|
||||
# -mstackrealign is included where necessary in the clang launcher scripts which are
|
||||
# pointed to by $CC, so we don't need to include it here.
|
||||
if [ $HOST = "arm-linux-androideabi" ]; then
|
||||
if [ "$HOST" = "arm-linux-androideabi" ]; then
|
||||
CFLAGS="$CFLAGS -march=armv7-a -mthumb"
|
||||
fi
|
||||
|
||||
if [ -n "${PREFIX:-}" ]; then
|
||||
abs_prefix=$(realpath $PREFIX)
|
||||
abs_prefix="$(realpath "$PREFIX")"
|
||||
CFLAGS="$CFLAGS -I$abs_prefix/include"
|
||||
LDFLAGS="$LDFLAGS -L$abs_prefix/lib"
|
||||
|
||||
|
|
@ -87,11 +87,13 @@ fi
|
|||
|
||||
# When compiling C++, some build systems will combine CFLAGS and CXXFLAGS, and some will
|
||||
# use CXXFLAGS alone.
|
||||
export CXXFLAGS=$CFLAGS
|
||||
export CXXFLAGS="$CFLAGS"
|
||||
|
||||
# Use the same variable name as conda-build
|
||||
if [ $(uname) = "Darwin" ]; then
|
||||
export CPU_COUNT=$(sysctl -n hw.ncpu)
|
||||
if [ "$(uname)" = "Darwin" ]; then
|
||||
CPU_COUNT="$(sysctl -n hw.ncpu)"
|
||||
export CPU_COUNT
|
||||
else
|
||||
export CPU_COUNT=$(nproc)
|
||||
CPU_COUNT="$(nproc)"
|
||||
export CPU_COUNT
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
import asyncio
|
||||
import argparse
|
||||
from glob import glob
|
||||
import os
|
||||
import re
|
||||
import shlex
|
||||
|
|
@ -13,6 +12,8 @@ import sys
|
|||
import sysconfig
|
||||
from asyncio import wait_for
|
||||
from contextlib import asynccontextmanager
|
||||
from datetime import datetime, timezone
|
||||
from glob import glob
|
||||
from os.path import basename, relpath
|
||||
from pathlib import Path
|
||||
from subprocess import CalledProcessError
|
||||
|
|
@ -20,11 +21,12 @@ from tempfile import TemporaryDirectory
|
|||
|
||||
|
||||
SCRIPT_NAME = Path(__file__).name
|
||||
CHECKOUT = Path(__file__).resolve().parent.parent
|
||||
ANDROID_DIR = CHECKOUT / "Android"
|
||||
ANDROID_DIR = Path(__file__).resolve().parent
|
||||
CHECKOUT = ANDROID_DIR.parent
|
||||
TESTBED_DIR = ANDROID_DIR / "testbed"
|
||||
CROSS_BUILD_DIR = CHECKOUT / "cross-build"
|
||||
|
||||
HOSTS = ["aarch64-linux-android", "x86_64-linux-android"]
|
||||
APP_ID = "org.python.testbed"
|
||||
DECODE_ARGS = ("UTF-8", "backslashreplace")
|
||||
|
||||
|
|
@ -58,12 +60,10 @@ def delete_glob(pattern):
|
|||
path.unlink()
|
||||
|
||||
|
||||
def subdir(name, *, clean=None):
|
||||
path = CROSS_BUILD_DIR / name
|
||||
if clean:
|
||||
delete_glob(path)
|
||||
def subdir(*parts, create=False):
|
||||
path = CROSS_BUILD_DIR.joinpath(*parts)
|
||||
if not path.exists():
|
||||
if clean is None:
|
||||
if not create:
|
||||
sys.exit(
|
||||
f"{path} does not exist. Create it by running the appropriate "
|
||||
f"`configure` subcommand of {SCRIPT_NAME}.")
|
||||
|
|
@ -123,7 +123,9 @@ def build_python_path():
|
|||
|
||||
|
||||
def configure_build_python(context):
|
||||
os.chdir(subdir("build", clean=context.clean))
|
||||
if context.clean:
|
||||
clean("build")
|
||||
os.chdir(subdir("build", create=True))
|
||||
|
||||
command = [relpath(CHECKOUT / "configure")]
|
||||
if context.args:
|
||||
|
|
@ -136,35 +138,33 @@ def make_build_python(context):
|
|||
run(["make", "-j", str(os.cpu_count())])
|
||||
|
||||
|
||||
def unpack_deps(host):
|
||||
def unpack_deps(host, prefix_dir):
|
||||
deps_url = "https://github.com/beeware/cpython-android-source-deps/releases/download"
|
||||
for name_ver in ["bzip2-1.0.8-2", "libffi-3.4.4-3", "openssl-3.0.15-4",
|
||||
"sqlite-3.45.3-3", "xz-5.4.6-1"]:
|
||||
"sqlite-3.49.1-0", "xz-5.4.6-1"]:
|
||||
filename = f"{name_ver}-{host}.tar.gz"
|
||||
download(f"{deps_url}/{name_ver}/{filename}")
|
||||
run(["tar", "-xf", filename])
|
||||
shutil.unpack_archive(filename, prefix_dir)
|
||||
os.remove(filename)
|
||||
|
||||
|
||||
def download(url, target_dir="."):
|
||||
out_path = f"{target_dir}/{basename(url)}"
|
||||
run(["curl", "-Lf", "-o", out_path, url])
|
||||
run(["curl", "-Lf", "--retry", "5", "--retry-all-errors", "-o", out_path, url])
|
||||
return out_path
|
||||
|
||||
|
||||
def configure_host_python(context):
|
||||
host_dir = subdir(context.host, clean=context.clean)
|
||||
if context.clean:
|
||||
clean(context.host)
|
||||
|
||||
host_dir = subdir(context.host, create=True)
|
||||
prefix_dir = host_dir / "prefix"
|
||||
if not prefix_dir.exists():
|
||||
prefix_dir.mkdir()
|
||||
os.chdir(prefix_dir)
|
||||
unpack_deps(context.host)
|
||||
|
||||
build_dir = host_dir / "build"
|
||||
build_dir.mkdir(exist_ok=True)
|
||||
os.chdir(build_dir)
|
||||
unpack_deps(context.host, prefix_dir)
|
||||
|
||||
os.chdir(host_dir)
|
||||
command = [
|
||||
# Basic cross-compiling configuration
|
||||
relpath(CHECKOUT / "configure"),
|
||||
|
|
@ -193,11 +193,10 @@ def make_host_python(context):
|
|||
# the build.
|
||||
host_dir = subdir(context.host)
|
||||
prefix_dir = host_dir / "prefix"
|
||||
delete_glob(f"{prefix_dir}/include/python*")
|
||||
delete_glob(f"{prefix_dir}/lib/libpython*")
|
||||
delete_glob(f"{prefix_dir}/lib/python*")
|
||||
for pattern in ("include/python*", "lib/libpython*", "lib/python*"):
|
||||
delete_glob(f"{prefix_dir}/{pattern}")
|
||||
|
||||
os.chdir(host_dir / "build")
|
||||
os.chdir(host_dir)
|
||||
run(["make", "-j", str(os.cpu_count())], host=context.host)
|
||||
run(["make", "install", f"prefix={prefix_dir}"], host=context.host)
|
||||
|
||||
|
|
@ -209,8 +208,13 @@ def build_all(context):
|
|||
step(context)
|
||||
|
||||
|
||||
def clean(host):
|
||||
delete_glob(CROSS_BUILD_DIR / host)
|
||||
|
||||
|
||||
def clean_all(context):
|
||||
delete_glob(CROSS_BUILD_DIR)
|
||||
for host in HOSTS + ["build"]:
|
||||
clean(host)
|
||||
|
||||
|
||||
def setup_sdk():
|
||||
|
|
@ -234,31 +238,26 @@ def setup_sdk():
|
|||
|
||||
# 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.
|
||||
# extract it from the Gradle GitHub repository.
|
||||
def setup_testbed():
|
||||
if all((TESTBED_DIR / path).exists() for path in [
|
||||
"gradlew", "gradlew.bat", "gradle/wrapper/gradle-wrapper.jar",
|
||||
]):
|
||||
paths = ["gradlew", "gradlew.bat", "gradle/wrapper/gradle-wrapper.jar"]
|
||||
if all((TESTBED_DIR / path).exists() for path in paths):
|
||||
return
|
||||
|
||||
ver_long = "8.7.0"
|
||||
ver_short = ver_long.removesuffix(".0")
|
||||
# The wrapper version isn't important, as any version of the wrapper can
|
||||
# download any version of Gradle. The Gradle version actually used for the
|
||||
# build is specified in testbed/gradle/wrapper/gradle-wrapper.properties.
|
||||
version = "8.9.0"
|
||||
|
||||
for filename in ["gradlew", "gradlew.bat"]:
|
||||
out_path = download(
|
||||
f"https://raw.githubusercontent.com/gradle/gradle/v{ver_long}/{filename}",
|
||||
TESTBED_DIR)
|
||||
for path in paths:
|
||||
out_path = TESTBED_DIR / path
|
||||
out_path.parent.mkdir(exist_ok=True)
|
||||
download(
|
||||
f"https://raw.githubusercontent.com/gradle/gradle/v{version}/{path}",
|
||||
out_path.parent,
|
||||
)
|
||||
os.chmod(out_path, 0o755)
|
||||
|
||||
with TemporaryDirectory(prefix=SCRIPT_NAME) as temp_dir:
|
||||
bin_zip = download(
|
||||
f"https://services.gradle.org/distributions/gradle-{ver_short}-bin.zip",
|
||||
temp_dir)
|
||||
outer_jar = f"gradle-{ver_short}/lib/plugins/gradle-wrapper-{ver_short}.jar"
|
||||
run(["unzip", "-d", temp_dir, bin_zip, outer_jar])
|
||||
run(["unzip", "-o", "-d", f"{TESTBED_DIR}/gradle/wrapper",
|
||||
f"{temp_dir}/{outer_jar}", "gradle-wrapper.jar"])
|
||||
|
||||
|
||||
# run_testbed will build the app automatically, but it's useful to have this as
|
||||
# a separate command to allow running the app outside of this script.
|
||||
|
|
@ -538,6 +537,73 @@ async def run_testbed(context):
|
|||
raise e.exceptions[0]
|
||||
|
||||
|
||||
def package_version(prefix_dir):
|
||||
patchlevel_glob = f"{prefix_dir}/include/python*/patchlevel.h"
|
||||
patchlevel_paths = glob(patchlevel_glob)
|
||||
if len(patchlevel_paths) != 1:
|
||||
sys.exit(f"{patchlevel_glob} matched {len(patchlevel_paths)} paths.")
|
||||
|
||||
for line in open(patchlevel_paths[0]):
|
||||
if match := re.fullmatch(r'\s*#define\s+PY_VERSION\s+"(.+)"\s*', line):
|
||||
version = match[1]
|
||||
break
|
||||
else:
|
||||
sys.exit(f"Failed to find Python version in {patchlevel_paths[0]}.")
|
||||
|
||||
# If not building against a tagged commit, add a timestamp to the version.
|
||||
# Follow the PyPA version number rules, as this will make it easier to
|
||||
# process with other tools.
|
||||
if version.endswith("+"):
|
||||
version += datetime.now(timezone.utc).strftime("%Y%m%d.%H%M%S")
|
||||
|
||||
return version
|
||||
|
||||
|
||||
def package(context):
|
||||
prefix_dir = subdir(context.host, "prefix")
|
||||
version = package_version(prefix_dir)
|
||||
|
||||
with TemporaryDirectory(prefix=SCRIPT_NAME) as temp_dir:
|
||||
temp_dir = Path(temp_dir)
|
||||
|
||||
# Include all tracked files from the Android directory.
|
||||
for line in run(
|
||||
["git", "ls-files"],
|
||||
cwd=ANDROID_DIR, capture_output=True, text=True, log=False,
|
||||
).stdout.splitlines():
|
||||
src = ANDROID_DIR / line
|
||||
dst = temp_dir / line
|
||||
dst.parent.mkdir(parents=True, exist_ok=True)
|
||||
shutil.copy2(src, dst, follow_symlinks=False)
|
||||
|
||||
# Include anything from the prefix directory which could be useful
|
||||
# either for embedding Python in an app, or building third-party
|
||||
# packages against it.
|
||||
for rel_dir, patterns in [
|
||||
("include", ["openssl*", "python*", "sqlite*"]),
|
||||
("lib", ["engines-3", "libcrypto*.so", "libpython*", "libsqlite*",
|
||||
"libssl*.so", "ossl-modules", "python*"]),
|
||||
("lib/pkgconfig", ["*crypto*", "*ssl*", "*python*", "*sqlite*"]),
|
||||
]:
|
||||
for pattern in patterns:
|
||||
for src in glob(f"{prefix_dir}/{rel_dir}/{pattern}"):
|
||||
dst = temp_dir / relpath(src, prefix_dir.parent)
|
||||
dst.parent.mkdir(parents=True, exist_ok=True)
|
||||
if Path(src).is_dir():
|
||||
shutil.copytree(
|
||||
src, dst, symlinks=True,
|
||||
ignore=lambda *args: ["__pycache__"]
|
||||
)
|
||||
else:
|
||||
shutil.copy2(src, dst, follow_symlinks=False)
|
||||
|
||||
dist_dir = subdir(context.host, "dist", create=True)
|
||||
package_path = shutil.make_archive(
|
||||
f"{dist_dir}/python-{version}-{context.host}", "gztar", temp_dir
|
||||
)
|
||||
print(f"Wrote {package_path}")
|
||||
|
||||
|
||||
# Handle SIGTERM the same way as SIGINT. This ensures that if we're terminated
|
||||
# by the buildbot worker, we'll make an attempt to clean up our subprocesses.
|
||||
def install_signal_handler():
|
||||
|
|
@ -550,6 +616,8 @@ def install_signal_handler():
|
|||
def parse_args():
|
||||
parser = argparse.ArgumentParser()
|
||||
subcommands = parser.add_subparsers(dest="subcommand")
|
||||
|
||||
# Subcommands
|
||||
build = subcommands.add_parser("build", help="Build everything")
|
||||
configure_build = subcommands.add_parser("configure-build",
|
||||
help="Run `configure` for the "
|
||||
|
|
@ -561,25 +629,27 @@ def parse_args():
|
|||
make_host = subcommands.add_parser("make-host",
|
||||
help="Run `make` for Android")
|
||||
subcommands.add_parser(
|
||||
"clean", help="Delete the cross-build directory")
|
||||
"clean", help="Delete all build and prefix directories")
|
||||
subcommands.add_parser(
|
||||
"build-testbed", help="Build the testbed app")
|
||||
test = subcommands.add_parser(
|
||||
"test", help="Run the test suite")
|
||||
package = subcommands.add_parser("package", help="Make a release package")
|
||||
|
||||
# Common arguments
|
||||
for subcommand in build, configure_build, configure_host:
|
||||
subcommand.add_argument(
|
||||
"--clean", action="store_true", default=False, dest="clean",
|
||||
help="Delete any relevant directories before building")
|
||||
for subcommand in build, configure_host, make_host:
|
||||
help="Delete the relevant build and prefix directories first")
|
||||
for subcommand in [build, configure_host, make_host, package]:
|
||||
subcommand.add_argument(
|
||||
"host", metavar="HOST",
|
||||
choices=["aarch64-linux-android", "x86_64-linux-android"],
|
||||
"host", metavar="HOST", choices=HOSTS,
|
||||
help="Host triplet: choices=[%(choices)s]")
|
||||
for subcommand in build, configure_build, configure_host:
|
||||
subcommand.add_argument("args", nargs="*",
|
||||
help="Extra arguments to pass to `configure`")
|
||||
|
||||
subcommands.add_parser(
|
||||
"build-testbed", help="Build the testbed app")
|
||||
test = subcommands.add_parser(
|
||||
"test", help="Run the test suite")
|
||||
# Test arguments
|
||||
test.add_argument(
|
||||
"-v", "--verbose", action="count", default=0,
|
||||
help="Show Gradle output, and non-Python logcat messages. "
|
||||
|
|
@ -608,14 +678,17 @@ def main():
|
|||
stream.reconfigure(line_buffering=True)
|
||||
|
||||
context = parse_args()
|
||||
dispatch = {"configure-build": configure_build_python,
|
||||
"make-build": make_build_python,
|
||||
"configure-host": configure_host_python,
|
||||
"make-host": make_host_python,
|
||||
"build": build_all,
|
||||
"clean": clean_all,
|
||||
"build-testbed": build_testbed,
|
||||
"test": run_testbed}
|
||||
dispatch = {
|
||||
"configure-build": configure_build_python,
|
||||
"make-build": make_build_python,
|
||||
"configure-host": configure_host_python,
|
||||
"make-host": make_host_python,
|
||||
"build": build_all,
|
||||
"clean": clean_all,
|
||||
"build-testbed": build_testbed,
|
||||
"test": run_testbed,
|
||||
"package": package,
|
||||
}
|
||||
|
||||
try:
|
||||
result = dispatch[context.subcommand](context)
|
||||
|
|
|
|||
17
Android/testbed/.gitignore
vendored
17
Android/testbed/.gitignore
vendored
|
|
@ -1,18 +1,19 @@
|
|||
# The Gradle wrapper should be downloaded by running `../android.py setup-testbed`.
|
||||
# The Gradle wrapper can be downloaded by running the `test` or `build-testbed`
|
||||
# commands of android.py.
|
||||
/gradlew
|
||||
/gradlew.bat
|
||||
/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# The repository's top-level .gitignore file ignores all .idea directories, but
|
||||
# we want to keep any files which can't be regenerated from the Gradle
|
||||
# configuration.
|
||||
!.idea/
|
||||
/.idea/*
|
||||
!/.idea/inspectionProfiles
|
||||
|
||||
*.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
|
||||
|
|
|
|||
8
Android/testbed/.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
8
Android/testbed/.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="AndroidLintGradleDependency" enabled="true" level="WEAK WARNING" enabled_by_default="true" editorAttributes="INFO_ATTRIBUTES" />
|
||||
<inspection_tool class="AndroidLintOldTargetApi" enabled="true" level="WEAK WARNING" enabled_by_default="true" editorAttributes="INFO_ATTRIBUTES" />
|
||||
<inspection_tool class="UnstableApiUsage" enabled="true" level="WEAK WARNING" enabled_by_default="true" editorAttributes="INFO_ATTRIBUTES" />
|
||||
</profile>
|
||||
</component>
|
||||
|
|
@ -6,28 +6,71 @@ plugins {
|
|||
id("org.jetbrains.kotlin.android")
|
||||
}
|
||||
|
||||
val PYTHON_DIR = file("../../..").canonicalPath
|
||||
val PYTHON_CROSS_DIR = "$PYTHON_DIR/cross-build"
|
||||
val ANDROID_DIR = file("../..")
|
||||
val PYTHON_DIR = ANDROID_DIR.parentFile!!
|
||||
val PYTHON_CROSS_DIR = file("$PYTHON_DIR/cross-build")
|
||||
val inSourceTree = (
|
||||
ANDROID_DIR.name == "Android" && file("$PYTHON_DIR/pyconfig.h.in").exists()
|
||||
)
|
||||
|
||||
val ABIS = mapOf(
|
||||
"arm64-v8a" to "aarch64-linux-android",
|
||||
"x86_64" to "x86_64-linux-android",
|
||||
).filter { file("$PYTHON_CROSS_DIR/${it.value}").exists() }
|
||||
if (ABIS.isEmpty()) {
|
||||
val KNOWN_ABIS = mapOf(
|
||||
"aarch64-linux-android" to "arm64-v8a",
|
||||
"x86_64-linux-android" to "x86_64",
|
||||
)
|
||||
|
||||
// Discover prefixes.
|
||||
val prefixes = ArrayList<File>()
|
||||
if (inSourceTree) {
|
||||
for ((triplet, _) in KNOWN_ABIS.entries) {
|
||||
val prefix = file("$PYTHON_CROSS_DIR/$triplet/prefix")
|
||||
if (prefix.exists()) {
|
||||
prefixes.add(prefix)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Testbed is inside a release package.
|
||||
val prefix = file("$ANDROID_DIR/prefix")
|
||||
if (prefix.exists()) {
|
||||
prefixes.add(prefix)
|
||||
}
|
||||
}
|
||||
if (prefixes.isEmpty()) {
|
||||
throw GradleException(
|
||||
"No Android ABIs found in $PYTHON_CROSS_DIR: see Android/README.md " +
|
||||
"for building instructions."
|
||||
"No Android prefixes found: see README.md for testing instructions"
|
||||
)
|
||||
}
|
||||
|
||||
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]
|
||||
// Detect Python versions and ABIs.
|
||||
lateinit var pythonVersion: String
|
||||
var abis = HashMap<File, String>()
|
||||
for ((i, prefix) in prefixes.withIndex()) {
|
||||
val libDir = file("$prefix/lib")
|
||||
val version = run {
|
||||
for (filename in libDir.list()!!) {
|
||||
"""python(\d+\.\d+)""".toRegex().matchEntire(filename)?.let {
|
||||
return@run it.groupValues[1]
|
||||
}
|
||||
}
|
||||
throw GradleException("Failed to find Python version in $libDir")
|
||||
}
|
||||
throw GradleException("Failed to find Python version")
|
||||
if (i == 0) {
|
||||
pythonVersion = version
|
||||
} else if (pythonVersion != version) {
|
||||
throw GradleException(
|
||||
"${prefixes[0]} is Python $pythonVersion, but $prefix is Python $version"
|
||||
)
|
||||
}
|
||||
|
||||
val libPythonDir = file("$libDir/python$pythonVersion")
|
||||
val triplet = run {
|
||||
for (filename in libPythonDir.list()!!) {
|
||||
"""_sysconfigdata__android_(.+).py""".toRegex().matchEntire(filename)?.let {
|
||||
return@run it.groupValues[1]
|
||||
}
|
||||
}
|
||||
throw GradleException("Failed to find Python triplet in $libPythonDir")
|
||||
}
|
||||
abis[prefix] = KNOWN_ABIS[triplet]!!
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -53,10 +96,16 @@ android {
|
|||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
|
||||
ndk.abiFilters.addAll(ABIS.keys)
|
||||
ndk.abiFilters.addAll(abis.values)
|
||||
externalNativeBuild.cmake.arguments(
|
||||
"-DPYTHON_CROSS_DIR=$PYTHON_CROSS_DIR",
|
||||
"-DPYTHON_VERSION=$PYTHON_VERSION",
|
||||
"-DPYTHON_PREFIX_DIR=" + if (inSourceTree) {
|
||||
// AGP uses the ${} syntax for its own purposes, so use a Jinja style
|
||||
// placeholder.
|
||||
"$PYTHON_CROSS_DIR/{{triplet}}/prefix"
|
||||
} else {
|
||||
prefixes[0]
|
||||
},
|
||||
"-DPYTHON_VERSION=$pythonVersion",
|
||||
"-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON",
|
||||
)
|
||||
|
||||
|
|
@ -133,24 +182,25 @@ dependencies {
|
|||
// Create some custom tasks to copy Python and its standard library from
|
||||
// elsewhere in the repository.
|
||||
androidComponents.onVariants { variant ->
|
||||
val pyPlusVer = "python$PYTHON_VERSION"
|
||||
val pyPlusVer = "python$pythonVersion"
|
||||
generateTask(variant, variant.sources.assets!!) {
|
||||
into("python") {
|
||||
// Include files such as pyconfig.h are used by some of the tests.
|
||||
into("include/$pyPlusVer") {
|
||||
for (triplet in ABIS.values) {
|
||||
from("$PYTHON_CROSS_DIR/$triplet/prefix/include/$pyPlusVer")
|
||||
for (prefix in prefixes) {
|
||||
from("$prefix/include/$pyPlusVer")
|
||||
}
|
||||
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
|
||||
}
|
||||
|
||||
into("lib/$pyPlusVer") {
|
||||
// To aid debugging, the source directory takes priority.
|
||||
from("$PYTHON_DIR/Lib")
|
||||
|
||||
// The cross-build directory provides ABI-specific files such as
|
||||
// sysconfigdata.
|
||||
for (triplet in ABIS.values) {
|
||||
from("$PYTHON_CROSS_DIR/$triplet/prefix/lib/$pyPlusVer")
|
||||
// To aid debugging, the source directory takes priority when
|
||||
// running inside a CPython source tree.
|
||||
if (inSourceTree) {
|
||||
from("$PYTHON_DIR/Lib")
|
||||
}
|
||||
for (prefix in prefixes) {
|
||||
from("$prefix/lib/$pyPlusVer")
|
||||
}
|
||||
|
||||
into("site-packages") {
|
||||
|
|
@ -164,9 +214,9 @@ androidComponents.onVariants { variant ->
|
|||
}
|
||||
|
||||
generateTask(variant, variant.sources.jniLibs!!) {
|
||||
for ((abi, triplet) in ABIS.entries) {
|
||||
for ((prefix, abi) in abis.entries) {
|
||||
into(abi) {
|
||||
from("$PYTHON_CROSS_DIR/$triplet/prefix/lib")
|
||||
from("$prefix/lib")
|
||||
include("libpython*.*.so")
|
||||
include("lib*_python.so")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,14 @@
|
|||
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)
|
||||
# Resolve variables from the command line.
|
||||
string(
|
||||
REPLACE {{triplet}} ${CMAKE_LIBRARY_ARCHITECTURE}
|
||||
PYTHON_PREFIX_DIR ${PYTHON_PREFIX_DIR}
|
||||
)
|
||||
|
||||
include_directories(${PYTHON_PREFIX_DIR}/include/python${PYTHON_VERSION})
|
||||
link_directories(${PYTHON_PREFIX_DIR}/lib)
|
||||
link_libraries(log python${PYTHON_VERSION})
|
||||
|
||||
add_library(main_activity SHARED main_activity.c)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
extend = "../.ruff.toml" # Inherit the project-wide settings
|
||||
|
||||
target-version = "py312" # Align with the version in oldest_supported_sphinx
|
||||
fix = true
|
||||
output-format = "full"
|
||||
line-length = 79
|
||||
extend-exclude = [
|
||||
"includes/*",
|
||||
# Temporary exclusions:
|
||||
|
|
|
|||
25
Doc/Makefile
25
Doc/Makefile
|
|
@ -14,15 +14,15 @@ PAPER =
|
|||
SOURCES =
|
||||
DISTVERSION = $(shell $(PYTHON) tools/extensions/patchlevel.py)
|
||||
REQUIREMENTS = requirements.txt
|
||||
SPHINXERRORHANDLING = -W
|
||||
SPHINXERRORHANDLING = --fail-on-warning
|
||||
|
||||
# Internal variables.
|
||||
PAPEROPT_a4 = -D latex_elements.papersize=a4paper
|
||||
PAPEROPT_letter = -D latex_elements.papersize=letterpaper
|
||||
PAPEROPT_a4 = --define latex_elements.papersize=a4paper
|
||||
PAPEROPT_letter = --define latex_elements.papersize=letterpaper
|
||||
|
||||
ALLSPHINXOPTS = -b $(BUILDER) \
|
||||
-d build/doctrees \
|
||||
-j $(JOBS) \
|
||||
ALLSPHINXOPTS = --builder $(BUILDER) \
|
||||
--doctree-dir build/doctrees \
|
||||
--jobs $(JOBS) \
|
||||
$(PAPEROPT_$(PAPER)) \
|
||||
$(SPHINXOPTS) $(SPHINXERRORHANDLING) \
|
||||
. build/$(BUILDER) $(SOURCES)
|
||||
|
|
@ -144,7 +144,7 @@ pydoc-topics: build
|
|||
|
||||
.PHONY: gettext
|
||||
gettext: BUILDER = gettext
|
||||
gettext: SPHINXOPTS += -d build/doctrees-gettext
|
||||
gettext: override SPHINXOPTS := --doctree-dir build/doctrees-gettext $(SPHINXOPTS)
|
||||
gettext: build
|
||||
|
||||
.PHONY: htmlview
|
||||
|
|
@ -172,7 +172,7 @@ venv:
|
|||
else \
|
||||
echo "Creating venv in $(VENVDIR)"; \
|
||||
if $(UV) --version >/dev/null 2>&1; then \
|
||||
$(UV) venv $(VENVDIR); \
|
||||
$(UV) venv --python=$(PYTHON) $(VENVDIR); \
|
||||
VIRTUAL_ENV=$(VENVDIR) $(UV) pip install -r $(REQUIREMENTS); \
|
||||
else \
|
||||
$(PYTHON) -m venv $(VENVDIR); \
|
||||
|
|
@ -204,6 +204,7 @@ dist-html:
|
|||
find dist -name 'python-$(DISTVERSION)-docs-html*' -exec rm -rf {} \;
|
||||
$(MAKE) html
|
||||
cp -pPR build/html dist/python-$(DISTVERSION)-docs-html
|
||||
rm -rf dist/python-$(DISTVERSION)-docs-html/_images/social_previews/
|
||||
tar -C dist -cf dist/python-$(DISTVERSION)-docs-html.tar python-$(DISTVERSION)-docs-html
|
||||
bzip2 -9 -k dist/python-$(DISTVERSION)-docs-html.tar
|
||||
(cd dist; zip -q -r -9 python-$(DISTVERSION)-docs-html.zip python-$(DISTVERSION)-docs-html)
|
||||
|
|
@ -300,20 +301,20 @@ serve:
|
|||
# By default, Sphinx only rebuilds pages where the page content has changed.
|
||||
# This means it doesn't always pick up changes to preferred link targets, etc
|
||||
# To ensure such changes are picked up, we build the published docs with
|
||||
# `-E` (to ignore the cached environment) and `-a` (to ignore already existing
|
||||
# output files)
|
||||
# ``--fresh-env`` (to ignore the cached environment) and ``--write-all``
|
||||
# (to ignore already existing output files)
|
||||
|
||||
# for development releases: always build
|
||||
.PHONY: autobuild-dev
|
||||
autobuild-dev: DISTVERSION = $(shell $(PYTHON) tools/extensions/patchlevel.py --short)
|
||||
autobuild-dev:
|
||||
$(MAKE) dist-no-html SPHINXOPTS='$(SPHINXOPTS) -Ea -A daily=1' DISTVERSION=$(DISTVERSION)
|
||||
$(MAKE) dist-no-html SPHINXOPTS='$(SPHINXOPTS) --fresh-env --write-all --html-define daily=1' DISTVERSION=$(DISTVERSION)
|
||||
|
||||
# for HTML-only rebuilds
|
||||
.PHONY: autobuild-dev-html
|
||||
autobuild-dev-html: DISTVERSION = $(shell $(PYTHON) tools/extensions/patchlevel.py --short)
|
||||
autobuild-dev-html:
|
||||
$(MAKE) dist-html SPHINXOPTS='$(SPHINXOPTS) -Ea -A daily=1' DISTVERSION=$(DISTVERSION)
|
||||
$(MAKE) dist-html SPHINXOPTS='$(SPHINXOPTS) --fresh-env --write-all --html-define daily=1' DISTVERSION=$(DISTVERSION)
|
||||
|
||||
# for stable releases: only build if not in pre-release stage (alpha, beta)
|
||||
# release candidate downloads are okay, since the stable tree can be in that stage
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
=====================
|
||||
About these documents
|
||||
=====================
|
||||
========================
|
||||
About this documentation
|
||||
========================
|
||||
|
||||
|
||||
These documents are generated from `reStructuredText`_ sources by `Sphinx`_, a
|
||||
document processor specifically written for the Python documentation.
|
||||
Python's documentation is generated from `reStructuredText`_ sources
|
||||
using `Sphinx`_, a documentation generator originally created for Python
|
||||
and now maintained as an independent project.
|
||||
|
||||
.. _reStructuredText: https://docutils.sourceforge.io/rst.html
|
||||
.. _Sphinx: https://www.sphinx-doc.org/
|
||||
|
|
@ -20,14 +21,14 @@ volunteers are always welcome!
|
|||
Many thanks go to:
|
||||
|
||||
* Fred L. Drake, Jr., the creator of the original Python documentation toolset
|
||||
and writer of much of the content;
|
||||
and author of much of the content;
|
||||
* the `Docutils <https://docutils.sourceforge.io/>`_ project for creating
|
||||
reStructuredText and the Docutils suite;
|
||||
* Fredrik Lundh for his Alternative Python Reference project from which Sphinx
|
||||
got many good ideas.
|
||||
|
||||
|
||||
Contributors to the Python Documentation
|
||||
Contributors to the Python documentation
|
||||
----------------------------------------
|
||||
|
||||
Many people have contributed to the Python language, the Python standard
|
||||
|
|
|
|||
|
|
@ -16,7 +16,20 @@ Allocating Objects on the Heap
|
|||
|
||||
Initialize a newly allocated object *op* with its type and initial
|
||||
reference. Returns the initialized object. Other fields of the object are
|
||||
not affected.
|
||||
not initialized. Despite its name, this function is unrelated to the
|
||||
object's :meth:`~object.__init__` method (:c:member:`~PyTypeObject.tp_init`
|
||||
slot). Specifically, this function does **not** call the object's
|
||||
:meth:`!__init__` method.
|
||||
|
||||
In general, consider this function to be a low-level routine. Use
|
||||
:c:member:`~PyTypeObject.tp_alloc` where possible.
|
||||
For implementing :c:member:`!tp_alloc` for your type, prefer
|
||||
:c:func:`PyType_GenericAlloc` or :c:func:`PyObject_New`.
|
||||
|
||||
.. note::
|
||||
|
||||
This function only initializes the object's memory corresponding to the
|
||||
initial :c:type:`PyObject` structure. It does not zero the rest.
|
||||
|
||||
|
||||
.. c:function:: PyVarObject* PyObject_InitVar(PyVarObject *op, PyTypeObject *type, Py_ssize_t size)
|
||||
|
|
@ -24,30 +37,107 @@ Allocating Objects on the Heap
|
|||
This does everything :c:func:`PyObject_Init` does, and also initializes the
|
||||
length information for a variable-size object.
|
||||
|
||||
.. note::
|
||||
|
||||
This function only initializes some of the object's memory. It does not
|
||||
zero the rest.
|
||||
|
||||
|
||||
.. c:macro:: PyObject_New(TYPE, typeobj)
|
||||
|
||||
Allocate a new Python object using the C structure type *TYPE*
|
||||
and the Python type object *typeobj* (``PyTypeObject*``).
|
||||
Fields not defined by the Python object header are not initialized.
|
||||
The caller will own the only reference to the object
|
||||
(i.e. its reference count will be one).
|
||||
The size of the memory allocation is determined from the
|
||||
:c:member:`~PyTypeObject.tp_basicsize` field of the type object.
|
||||
Allocates a new Python object using the C structure type *TYPE* and the
|
||||
Python type object *typeobj* (``PyTypeObject*``) by calling
|
||||
:c:func:`PyObject_Malloc` to allocate memory and initializing it like
|
||||
:c:func:`PyObject_Init`. The caller will own the only reference to the
|
||||
object (i.e. its reference count will be one).
|
||||
|
||||
Avoid calling this directly to allocate memory for an object; call the type's
|
||||
:c:member:`~PyTypeObject.tp_alloc` slot instead.
|
||||
|
||||
When populating a type's :c:member:`~PyTypeObject.tp_alloc` slot,
|
||||
:c:func:`PyType_GenericAlloc` is preferred over a custom function that
|
||||
simply calls this macro.
|
||||
|
||||
This macro does not call :c:member:`~PyTypeObject.tp_alloc`,
|
||||
:c:member:`~PyTypeObject.tp_new` (:meth:`~object.__new__`), or
|
||||
:c:member:`~PyTypeObject.tp_init` (:meth:`~object.__init__`).
|
||||
|
||||
This cannot be used for objects with :c:macro:`Py_TPFLAGS_HAVE_GC` set in
|
||||
:c:member:`~PyTypeObject.tp_flags`; use :c:macro:`PyObject_GC_New` instead.
|
||||
|
||||
Memory allocated by this macro must be freed with :c:func:`PyObject_Free`
|
||||
(usually called via the object's :c:member:`~PyTypeObject.tp_free` slot).
|
||||
|
||||
.. note::
|
||||
|
||||
The returned memory is not guaranteed to have been completely zeroed
|
||||
before it was initialized.
|
||||
|
||||
.. note::
|
||||
|
||||
This macro does not construct a fully initialized object of the given
|
||||
type; it merely allocates memory and prepares it for further
|
||||
initialization by :c:member:`~PyTypeObject.tp_init`. To construct a
|
||||
fully initialized object, call *typeobj* instead. For example::
|
||||
|
||||
PyObject *foo = PyObject_CallNoArgs((PyObject *)&PyFoo_Type);
|
||||
|
||||
.. seealso::
|
||||
|
||||
* :c:func:`PyObject_Free`
|
||||
* :c:macro:`PyObject_GC_New`
|
||||
* :c:func:`PyType_GenericAlloc`
|
||||
* :c:member:`~PyTypeObject.tp_alloc`
|
||||
|
||||
|
||||
.. c:macro:: PyObject_NewVar(TYPE, typeobj, size)
|
||||
|
||||
Allocate a new Python object using the C structure type *TYPE* and the
|
||||
Python type object *typeobj* (``PyTypeObject*``).
|
||||
Fields not defined by the Python object header
|
||||
are not initialized. The allocated memory allows for the *TYPE* structure
|
||||
plus *size* (``Py_ssize_t``) fields of the size
|
||||
given by the :c:member:`~PyTypeObject.tp_itemsize` field of
|
||||
*typeobj*. This is useful for implementing objects like tuples, which are
|
||||
able to determine their size at construction time. Embedding the array of
|
||||
fields into the same allocation decreases the number of allocations,
|
||||
improving the memory management efficiency.
|
||||
Like :c:macro:`PyObject_New` except:
|
||||
|
||||
* It allocates enough memory for the *TYPE* structure plus *size*
|
||||
(``Py_ssize_t``) fields of the size given by the
|
||||
:c:member:`~PyTypeObject.tp_itemsize` field of *typeobj*.
|
||||
* The memory is initialized like :c:func:`PyObject_InitVar`.
|
||||
|
||||
This is useful for implementing objects like tuples, which are able to
|
||||
determine their size at construction time. Embedding the array of fields
|
||||
into the same allocation decreases the number of allocations, improving the
|
||||
memory management efficiency.
|
||||
|
||||
Avoid calling this directly to allocate memory for an object; call the type's
|
||||
:c:member:`~PyTypeObject.tp_alloc` slot instead.
|
||||
|
||||
When populating a type's :c:member:`~PyTypeObject.tp_alloc` slot,
|
||||
:c:func:`PyType_GenericAlloc` is preferred over a custom function that
|
||||
simply calls this macro.
|
||||
|
||||
This cannot be used for objects with :c:macro:`Py_TPFLAGS_HAVE_GC` set in
|
||||
:c:member:`~PyTypeObject.tp_flags`; use :c:macro:`PyObject_GC_NewVar`
|
||||
instead.
|
||||
|
||||
Memory allocated by this function must be freed with :c:func:`PyObject_Free`
|
||||
(usually called via the object's :c:member:`~PyTypeObject.tp_free` slot).
|
||||
|
||||
.. note::
|
||||
|
||||
The returned memory is not guaranteed to have been completely zeroed
|
||||
before it was initialized.
|
||||
|
||||
.. note::
|
||||
|
||||
This macro does not construct a fully initialized object of the given
|
||||
type; it merely allocates memory and prepares it for further
|
||||
initialization by :c:member:`~PyTypeObject.tp_init`. To construct a
|
||||
fully initialized object, call *typeobj* instead. For example::
|
||||
|
||||
PyObject *list_instance = PyObject_CallNoArgs((PyObject *)&PyList_Type);
|
||||
|
||||
.. seealso::
|
||||
|
||||
* :c:func:`PyObject_Free`
|
||||
* :c:macro:`PyObject_GC_NewVar`
|
||||
* :c:func:`PyType_GenericAlloc`
|
||||
* :c:member:`~PyTypeObject.tp_alloc`
|
||||
|
||||
|
||||
.. c:function:: void PyObject_Del(void *op)
|
||||
|
|
|
|||
|
|
@ -6,9 +6,13 @@
|
|||
API and ABI Versioning
|
||||
***********************
|
||||
|
||||
|
||||
Build-time version constants
|
||||
----------------------------
|
||||
|
||||
CPython exposes its version number in the following macros.
|
||||
Note that these correspond to the version code is **built** with,
|
||||
not necessarily the version used at **run time**.
|
||||
Note that these correspond to the version code is **built** with.
|
||||
See :c:var:`Py_Version` for the version used at **run time**.
|
||||
|
||||
See :ref:`stable` for a discussion of API and ABI stability across versions.
|
||||
|
||||
|
|
@ -37,37 +41,83 @@ See :ref:`stable` for a discussion of API and ABI stability across versions.
|
|||
.. c:macro:: PY_VERSION_HEX
|
||||
|
||||
The Python version number encoded in a single integer.
|
||||
See :c:func:`Py_PACK_FULL_VERSION` for the encoding details.
|
||||
|
||||
The underlying version information can be found by treating it as a 32 bit
|
||||
number in the following manner:
|
||||
Use this for numeric comparisons, for example,
|
||||
``#if PY_VERSION_HEX >= ...``.
|
||||
|
||||
+-------+-------------------------+-------------------------+--------------------------+
|
||||
| Bytes | Bits (big endian order) | Meaning | Value for ``3.4.1a2`` |
|
||||
+=======+=========================+=========================+==========================+
|
||||
| 1 | 1-8 | ``PY_MAJOR_VERSION`` | ``0x03`` |
|
||||
+-------+-------------------------+-------------------------+--------------------------+
|
||||
| 2 | 9-16 | ``PY_MINOR_VERSION`` | ``0x04`` |
|
||||
+-------+-------------------------+-------------------------+--------------------------+
|
||||
| 3 | 17-24 | ``PY_MICRO_VERSION`` | ``0x01`` |
|
||||
+-------+-------------------------+-------------------------+--------------------------+
|
||||
| 4 | 25-28 | ``PY_RELEASE_LEVEL`` | ``0xA`` |
|
||||
+ +-------------------------+-------------------------+--------------------------+
|
||||
| | 29-32 | ``PY_RELEASE_SERIAL`` | ``0x2`` |
|
||||
+-------+-------------------------+-------------------------+--------------------------+
|
||||
|
||||
Thus ``3.4.1a2`` is hexversion ``0x030401a2`` and ``3.10.0`` is
|
||||
hexversion ``0x030a00f0``.
|
||||
|
||||
Use this for numeric comparisons, e.g. ``#if PY_VERSION_HEX >= ...``.
|
||||
|
||||
This version is also available via the symbol :c:var:`Py_Version`.
|
||||
Run-time version
|
||||
----------------
|
||||
|
||||
.. c:var:: const unsigned long Py_Version
|
||||
|
||||
The Python runtime version number encoded in a single constant integer, with
|
||||
the same format as the :c:macro:`PY_VERSION_HEX` macro.
|
||||
The Python runtime version number encoded in a single constant integer.
|
||||
See :c:func:`Py_PACK_FULL_VERSION` for the encoding details.
|
||||
This contains the Python version used at run time.
|
||||
|
||||
Use this for numeric comparisons, for example, ``if (Py_Version >= ...)``.
|
||||
|
||||
.. versionadded:: 3.11
|
||||
|
||||
All the given macros are defined in :source:`Include/patchlevel.h`.
|
||||
|
||||
Bit-packing macros
|
||||
------------------
|
||||
|
||||
.. c:function:: uint32_t Py_PACK_FULL_VERSION(int major, int minor, int micro, int release_level, int release_serial)
|
||||
|
||||
Return the given version, encoded as a single 32-bit integer with
|
||||
the following structure:
|
||||
|
||||
+------------------+-------+----------------+-----------+--------------------------+
|
||||
| | No. | | | Example values |
|
||||
| | of | | +-------------+------------+
|
||||
| Argument | bits | Bit mask | Bit shift | ``3.4.1a2`` | ``3.10.0`` |
|
||||
+==================+=======+================+===========+=============+============+
|
||||
| *major* | 8 | ``0xFF000000`` | 24 | ``0x03`` | ``0x03`` |
|
||||
+------------------+-------+----------------+-----------+-------------+------------+
|
||||
| *minor* | 8 | ``0x00FF0000`` | 16 | ``0x04`` | ``0x0A`` |
|
||||
+------------------+-------+----------------+-----------+-------------+------------+
|
||||
| *micro* | 8 | ``0x0000FF00`` | 8 | ``0x01`` | ``0x00`` |
|
||||
+------------------+-------+----------------+-----------+-------------+------------+
|
||||
| *release_level* | 4 | ``0x000000F0`` | 4 | ``0xA`` | ``0xF`` |
|
||||
+------------------+-------+----------------+-----------+-------------+------------+
|
||||
| *release_serial* | 4 | ``0x0000000F`` | 0 | ``0x2`` | ``0x0`` |
|
||||
+------------------+-------+----------------+-----------+-------------+------------+
|
||||
|
||||
For example:
|
||||
|
||||
+-------------+------------------------------------+-----------------+
|
||||
| Version | ``Py_PACK_FULL_VERSION`` arguments | Encoded version |
|
||||
+=============+====================================+=================+
|
||||
| ``3.4.1a2`` | ``(3, 4, 1, 0xA, 2)`` | ``0x030401a2`` |
|
||||
+-------------+------------------------------------+-----------------+
|
||||
| ``3.10.0`` | ``(3, 10, 0, 0xF, 0)`` | ``0x030a00f0`` |
|
||||
+-------------+------------------------------------+-----------------+
|
||||
|
||||
Out-of range bits in the arguments are ignored.
|
||||
That is, the macro can be defined as:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
#ifndef Py_PACK_FULL_VERSION
|
||||
#define Py_PACK_FULL_VERSION(X, Y, Z, LEVEL, SERIAL) ( \
|
||||
(((X) & 0xff) << 24) | \
|
||||
(((Y) & 0xff) << 16) | \
|
||||
(((Z) & 0xff) << 8) | \
|
||||
(((LEVEL) & 0xf) << 4) | \
|
||||
(((SERIAL) & 0xf) << 0))
|
||||
#endif
|
||||
|
||||
``Py_PACK_FULL_VERSION`` is primarily a macro, intended for use in
|
||||
``#if`` directives, but it is also available as an exported function.
|
||||
|
||||
.. versionadded:: 3.14
|
||||
|
||||
.. c:function:: uint32_t Py_PACK_VERSION(int major, int minor)
|
||||
|
||||
Equivalent to ``Py_PACK_FULL_VERSION(major, minor, 0, 0, 0)``.
|
||||
The result does not correspond to any Python release, but is useful
|
||||
in numeric comparisons.
|
||||
|
||||
.. versionadded:: 3.14
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
Parsing arguments and building values
|
||||
=====================================
|
||||
|
||||
These functions are useful when creating your own extensions functions and
|
||||
These functions are useful when creating your own extension functions and
|
||||
methods. Additional information and examples are available in
|
||||
:ref:`extending-index`.
|
||||
|
||||
|
|
@ -113,14 +113,18 @@ There are three ways strings and buffers can be converted to C:
|
|||
``z`` (:class:`str` or ``None``) [const char \*]
|
||||
Like ``s``, but the Python object may also be ``None``, in which case the C
|
||||
pointer is set to ``NULL``.
|
||||
It is the same as ``s?`` with the C pointer was initialized to ``NULL``.
|
||||
|
||||
``z*`` (:class:`str`, :term:`bytes-like object` or ``None``) [Py_buffer]
|
||||
Like ``s*``, but the Python object may also be ``None``, in which case the
|
||||
``buf`` member of the :c:type:`Py_buffer` structure is set to ``NULL``.
|
||||
It is the same as ``s*?`` with the ``buf`` member of the :c:type:`Py_buffer`
|
||||
structure was initialized to ``NULL``.
|
||||
|
||||
``z#`` (:class:`str`, read-only :term:`bytes-like object` or ``None``) [const char \*, :c:type:`Py_ssize_t`]
|
||||
Like ``s#``, but the Python object may also be ``None``, in which case the C
|
||||
pointer is set to ``NULL``.
|
||||
It is the same as ``s#?`` with the C pointer was initialized to ``NULL``.
|
||||
|
||||
``y`` (read-only :term:`bytes-like object`) [const char \*]
|
||||
This format converts a bytes-like object to a C pointer to a
|
||||
|
|
@ -229,12 +233,24 @@ There are three ways strings and buffers can be converted to C:
|
|||
Numbers
|
||||
-------
|
||||
|
||||
These formats allow representing Python numbers or single characters as C numbers.
|
||||
Formats that require :class:`int`, :class:`float` or :class:`complex` can
|
||||
also use the corresponding special methods :meth:`~object.__index__`,
|
||||
:meth:`~object.__float__` or :meth:`~object.__complex__` to convert
|
||||
the Python object to the required type.
|
||||
|
||||
For signed integer formats, :exc:`OverflowError` is raised if the value
|
||||
is out of range for the C type.
|
||||
For unsigned integer formats, no range checking is done --- the
|
||||
most significant bits are silently truncated when the receiving field is too
|
||||
small to receive the value.
|
||||
|
||||
``b`` (:class:`int`) [unsigned char]
|
||||
Convert a nonnegative Python integer to an unsigned tiny int, stored in a C
|
||||
Convert a nonnegative Python integer to an unsigned tiny integer, stored in a C
|
||||
:c:expr:`unsigned char`.
|
||||
|
||||
``B`` (:class:`int`) [unsigned char]
|
||||
Convert a Python integer to a tiny int without overflow checking, stored in a C
|
||||
Convert a Python integer to a tiny integer without overflow checking, stored in a C
|
||||
:c:expr:`unsigned char`.
|
||||
|
||||
``h`` (:class:`int`) [short int]
|
||||
|
|
@ -258,6 +274,9 @@ Numbers
|
|||
Convert a Python integer to a C :c:expr:`unsigned long` without
|
||||
overflow checking.
|
||||
|
||||
.. versionchanged:: 3.14
|
||||
Use :meth:`~object.__index__` if available.
|
||||
|
||||
``L`` (:class:`int`) [long long]
|
||||
Convert a Python integer to a C :c:expr:`long long`.
|
||||
|
||||
|
|
@ -265,6 +284,9 @@ Numbers
|
|||
Convert a Python integer to a C :c:expr:`unsigned long long`
|
||||
without overflow checking.
|
||||
|
||||
.. versionchanged:: 3.14
|
||||
Use :meth:`~object.__index__` if available.
|
||||
|
||||
``n`` (:class:`int`) [:c:type:`Py_ssize_t`]
|
||||
Convert a Python integer to a C :c:type:`Py_ssize_t`.
|
||||
|
||||
|
|
@ -307,7 +329,7 @@ Other objects
|
|||
|
||||
.. _o_ampersand:
|
||||
|
||||
``O&`` (object) [*converter*, *anything*]
|
||||
``O&`` (object) [*converter*, *address*]
|
||||
Convert a Python object to a C variable through a *converter* function. This
|
||||
takes two arguments: the first is a function, the second is the address of a C
|
||||
variable (of arbitrary type), converted to :c:expr:`void *`. The *converter*
|
||||
|
|
@ -321,14 +343,20 @@ Other objects
|
|||
the conversion has failed. When the conversion fails, the *converter* function
|
||||
should raise an exception and leave the content of *address* unmodified.
|
||||
|
||||
If the *converter* returns ``Py_CLEANUP_SUPPORTED``, it may get called a
|
||||
.. c:macro:: Py_CLEANUP_SUPPORTED
|
||||
:no-typesetting:
|
||||
|
||||
If the *converter* returns :c:macro:`!Py_CLEANUP_SUPPORTED`, it may get called a
|
||||
second time if the argument parsing eventually fails, giving the converter a
|
||||
chance to release any memory that it had already allocated. In this second
|
||||
call, the *object* parameter will be ``NULL``; *address* will have the same value
|
||||
as in the original call.
|
||||
|
||||
Examples of converters: :c:func:`PyUnicode_FSConverter` and
|
||||
:c:func:`PyUnicode_FSDecoder`.
|
||||
|
||||
.. versionchanged:: 3.1
|
||||
``Py_CLEANUP_SUPPORTED`` was added.
|
||||
:c:macro:`!Py_CLEANUP_SUPPORTED` was added.
|
||||
|
||||
``p`` (:class:`bool`) [int]
|
||||
Tests the value passed in for truth (a boolean **p**\ redicate) and converts
|
||||
|
|
@ -339,16 +367,36 @@ Other objects
|
|||
|
||||
.. versionadded:: 3.3
|
||||
|
||||
``(items)`` (:class:`tuple`) [*matching-items*]
|
||||
The object must be a Python sequence whose length is the number of format units
|
||||
``(items)`` (sequence) [*matching-items*]
|
||||
The object must be a Python sequence (except :class:`str`, :class:`bytes`
|
||||
or :class:`bytearray`) whose length is the number of format units
|
||||
in *items*. The C arguments must correspond to the individual format units in
|
||||
*items*. Format units for sequences may be nested.
|
||||
|
||||
It is possible to pass "long" integers (integers whose value exceeds the
|
||||
platform's :c:macro:`LONG_MAX`) however no proper range checking is done --- the
|
||||
most significant bits are silently truncated when the receiving field is too
|
||||
small to receive the value (actually, the semantics are inherited from downcasts
|
||||
in C --- your mileage may vary).
|
||||
If *items* contains format units which store a :ref:`borrowed buffer
|
||||
<c-arg-borrowed-buffer>` (``s``, ``s#``, ``z``, ``z#``, ``y``, or ``y#``)
|
||||
or a :term:`borrowed reference` (``S``, ``Y``, ``U``, ``O``, or ``O!``),
|
||||
the object must be a Python tuple.
|
||||
The *converter* for the ``O&`` format unit in *items* must not store
|
||||
a borrowed buffer or a borrowed reference.
|
||||
|
||||
.. versionchanged:: 3.14
|
||||
:class:`str` and :class:`bytearray` objects no longer accepted as a sequence.
|
||||
|
||||
.. deprecated:: 3.14
|
||||
Non-tuple sequences are deprecated if *items* contains format units
|
||||
which store a borrowed buffer or a borrowed reference.
|
||||
|
||||
``unit?`` (anything or ``None``) [*matching-variable(s)*]
|
||||
``?`` modifies the behavior of the preceding format unit.
|
||||
The C variable(s) corresponding to that parameter should be initialized
|
||||
to their default value --- when the argument is ``None``,
|
||||
:c:func:`PyArg_ParseTuple` does not touch the contents of the corresponding
|
||||
C variable(s).
|
||||
If the argument is not ``None``, it is parsed according to the specified
|
||||
format unit.
|
||||
|
||||
.. versionadded:: 3.14
|
||||
|
||||
A few other characters have a meaning in a format string. These may not occur
|
||||
inside nested parentheses. They are:
|
||||
|
|
@ -627,12 +675,18 @@ Building values
|
|||
``L`` (:class:`int`) [long long]
|
||||
Convert a C :c:expr:`long long` to a Python integer object.
|
||||
|
||||
.. _capi-py-buildvalue-format-K:
|
||||
|
||||
``K`` (:class:`int`) [unsigned long long]
|
||||
Convert a C :c:expr:`unsigned long long` to a Python integer object.
|
||||
|
||||
``n`` (:class:`int`) [:c:type:`Py_ssize_t`]
|
||||
Convert a C :c:type:`Py_ssize_t` to a Python integer.
|
||||
|
||||
``p`` (:class:`bool`) [int]
|
||||
Convert a C :c:expr:`int` to a Python :class:`bool` object.
|
||||
.. versionadded:: 3.14
|
||||
|
||||
``c`` (:class:`bytes` of length 1) [char]
|
||||
Convert a C :c:expr:`int` representing a byte to a Python :class:`bytes` object of
|
||||
length 1.
|
||||
|
|
|
|||
|
|
@ -26,17 +26,19 @@ characteristic of being backed by a possibly large memory buffer. It is
|
|||
then desirable, in some situations, to access that buffer directly and
|
||||
without intermediate copying.
|
||||
|
||||
Python provides such a facility at the C level in the form of the :ref:`buffer
|
||||
protocol <bufferobjects>`. This protocol has two sides:
|
||||
Python provides such a facility at the C and Python level in the form of the
|
||||
:ref:`buffer protocol <bufferobjects>`. This protocol has two sides:
|
||||
|
||||
.. index:: single: PyBufferProcs (C type)
|
||||
|
||||
- on the producer side, a type can export a "buffer interface" which allows
|
||||
objects of that type to expose information about their underlying buffer.
|
||||
This interface is described in the section :ref:`buffer-structs`;
|
||||
This interface is described in the section :ref:`buffer-structs`; for
|
||||
Python see :ref:`python-buffer-protocol`.
|
||||
|
||||
- on the consumer side, several means are available to obtain a pointer to
|
||||
the raw underlying data of an object (for example a method parameter).
|
||||
the raw underlying data of an object (for example a method parameter). For
|
||||
Python see :class:`memoryview`.
|
||||
|
||||
Simple objects such as :class:`bytes` and :class:`bytearray` expose their
|
||||
underlying buffer in byte-oriented form. Other forms are possible; for example,
|
||||
|
|
@ -62,6 +64,10 @@ In both cases, :c:func:`PyBuffer_Release` must be called when the buffer
|
|||
isn't needed anymore. Failure to do so could lead to various issues such as
|
||||
resource leaks.
|
||||
|
||||
.. versionadded:: 3.12
|
||||
|
||||
The buffer protocol is now accessible in Python, see
|
||||
:ref:`python-buffer-protocol` and :class:`memoryview`.
|
||||
|
||||
.. _buffer-structure:
|
||||
|
||||
|
|
|
|||
|
|
@ -74,6 +74,11 @@ Direct API functions
|
|||
.. c:function:: int PyByteArray_Resize(PyObject *bytearray, Py_ssize_t len)
|
||||
|
||||
Resize the internal buffer of *bytearray* to *len*.
|
||||
Failure is a ``-1`` return with an exception set.
|
||||
|
||||
.. versionchanged:: 3.14
|
||||
A negative *len* will now result in an exception being set and -1 returned.
|
||||
|
||||
|
||||
Macros
|
||||
^^^^^^
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ Dictionary Objects
|
|||
Prefer the :c:func:`PyDict_GetItemWithError` function instead.
|
||||
|
||||
.. versionchanged:: 3.10
|
||||
Calling this API without :term:`GIL` held had been allowed for historical
|
||||
Calling this API without an :term:`attached thread state` had been allowed for historical
|
||||
reason. It is no longer allowed.
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -413,7 +413,7 @@ Querying the error indicator
|
|||
own a reference to the return value, so you do not need to :c:func:`Py_DECREF`
|
||||
it.
|
||||
|
||||
The caller must hold the GIL.
|
||||
The caller must have an :term:`attached thread state`.
|
||||
|
||||
.. note::
|
||||
|
||||
|
|
@ -675,7 +675,7 @@ Signal Handling
|
|||
|
||||
.. note::
|
||||
This function is async-signal-safe. It can be called without
|
||||
the :term:`GIL` and from a C signal handler.
|
||||
an :term:`attached thread state` and from a C signal handler.
|
||||
|
||||
|
||||
.. c:function:: int PyErr_SetInterruptEx(int signum)
|
||||
|
|
@ -702,7 +702,7 @@ Signal Handling
|
|||
|
||||
.. note::
|
||||
This function is async-signal-safe. It can be called without
|
||||
the :term:`GIL` and from a C signal handler.
|
||||
an :term:`attached thread state` and from a C signal handler.
|
||||
|
||||
.. versionadded:: 3.10
|
||||
|
||||
|
|
@ -853,12 +853,23 @@ The following functions are used to create and modify Unicode exceptions from C.
|
|||
*\*start*. *start* must not be ``NULL``. Return ``0`` on success, ``-1`` on
|
||||
failure.
|
||||
|
||||
If the :attr:`UnicodeError.object` is an empty sequence, the resulting
|
||||
*start* is ``0``. Otherwise, it is clipped to ``[0, len(object) - 1]``.
|
||||
|
||||
.. seealso:: :attr:`UnicodeError.start`
|
||||
|
||||
.. c:function:: int PyUnicodeDecodeError_SetStart(PyObject *exc, Py_ssize_t start)
|
||||
int PyUnicodeEncodeError_SetStart(PyObject *exc, Py_ssize_t start)
|
||||
int PyUnicodeTranslateError_SetStart(PyObject *exc, Py_ssize_t start)
|
||||
|
||||
Set the *start* attribute of the given exception object to *start*. Return
|
||||
``0`` on success, ``-1`` on failure.
|
||||
Set the *start* attribute of the given exception object to *start*.
|
||||
Return ``0`` on success, ``-1`` on failure.
|
||||
|
||||
.. note::
|
||||
|
||||
While passing a negative *start* does not raise an exception,
|
||||
the corresponding getters will not consider it as a relative
|
||||
offset.
|
||||
|
||||
.. c:function:: int PyUnicodeDecodeError_GetEnd(PyObject *exc, Py_ssize_t *end)
|
||||
int PyUnicodeEncodeError_GetEnd(PyObject *exc, Py_ssize_t *end)
|
||||
|
|
@ -868,6 +879,9 @@ The following functions are used to create and modify Unicode exceptions from C.
|
|||
*\*end*. *end* must not be ``NULL``. Return ``0`` on success, ``-1`` on
|
||||
failure.
|
||||
|
||||
If the :attr:`UnicodeError.object` is an empty sequence, the resulting
|
||||
*end* is ``0``. Otherwise, it is clipped to ``[1, len(object)]``.
|
||||
|
||||
.. c:function:: int PyUnicodeDecodeError_SetEnd(PyObject *exc, Py_ssize_t end)
|
||||
int PyUnicodeEncodeError_SetEnd(PyObject *exc, Py_ssize_t end)
|
||||
int PyUnicodeTranslateError_SetEnd(PyObject *exc, Py_ssize_t end)
|
||||
|
|
@ -875,6 +889,8 @@ The following functions are used to create and modify Unicode exceptions from C.
|
|||
Set the *end* attribute of the given exception object to *end*. Return ``0``
|
||||
on success, ``-1`` on failure.
|
||||
|
||||
.. seealso:: :attr:`UnicodeError.end`
|
||||
|
||||
.. c:function:: PyObject* PyUnicodeDecodeError_GetReason(PyObject *exc)
|
||||
PyObject* PyUnicodeEncodeError_GetReason(PyObject *exc)
|
||||
PyObject* PyUnicodeTranslateError_GetReason(PyObject *exc)
|
||||
|
|
@ -905,11 +921,7 @@ because the :ref:`call protocol <call>` takes care of recursion handling.
|
|||
|
||||
Marks a point where a recursive C-level call is about to be performed.
|
||||
|
||||
If :c:macro:`!USE_STACKCHECK` is defined, this function checks if the OS
|
||||
stack overflowed using :c:func:`PyOS_CheckStack`. If this is the case, it
|
||||
sets a :exc:`MemoryError` and returns a nonzero value.
|
||||
|
||||
The function then checks if the recursion limit is reached. If this is the
|
||||
The function then checks if the stack limit is reached. If this is the
|
||||
case, a :exc:`RecursionError` is set and a nonzero value is returned.
|
||||
Otherwise, zero is returned.
|
||||
|
||||
|
|
|
|||
|
|
@ -96,6 +96,9 @@ NaNs (if such things exist on the platform) isn't handled correctly, and
|
|||
attempting to unpack a bytes string containing an IEEE INF or NaN will raise an
|
||||
exception.
|
||||
|
||||
Note that NaNs type may not be preserved on IEEE platforms (silent NaN become
|
||||
quiet), for example on x86 systems in 32-bit mode.
|
||||
|
||||
On non-IEEE platforms with more precision, or larger dynamic range, than IEEE
|
||||
754 supports, not all values can be packed; on non-IEEE platforms with less
|
||||
precision, or smaller dynamic range, not all values can be unpacked. What
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@ See also :ref:`Reflection <reflection>`.
|
|||
.. versionadded:: 3.11
|
||||
|
||||
.. versionchanged:: 3.13
|
||||
As part of :pep:`667`, return a proxy object for optimized scopes.
|
||||
As part of :pep:`667`, return an instance of :c:var:`PyFrameLocalsProxy_Type`.
|
||||
|
||||
|
||||
.. c:function:: int PyFrame_GetLineNumber(PyFrameObject *frame)
|
||||
|
|
@ -140,6 +140,26 @@ See also :ref:`Reflection <reflection>`.
|
|||
Return the line number that *frame* is currently executing.
|
||||
|
||||
|
||||
Frame Locals Proxies
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. versionadded:: 3.13
|
||||
|
||||
The :attr:`~frame.f_locals` attribute on a :ref:`frame object <frame-objects>`
|
||||
is an instance of a "frame-locals proxy". The proxy object exposes a
|
||||
write-through view of the underlying locals dictionary for the frame. This
|
||||
ensures that the variables exposed by ``f_locals`` are always up to date with
|
||||
the live local variables in the frame itself.
|
||||
|
||||
See :pep:`667` for more information.
|
||||
|
||||
.. c:var:: PyTypeObject PyFrameLocalsProxy_Type
|
||||
|
||||
The type of frame :func:`locals` proxy objects.
|
||||
|
||||
.. c:function:: int PyFrameLocalsProxy_Check(PyObject *obj)
|
||||
|
||||
Return non-zero if *obj* is a frame :func:`locals` proxy.
|
||||
|
||||
Internal Frames
|
||||
^^^^^^^^^^^^^^^
|
||||
|
|
|
|||
|
|
@ -145,12 +145,13 @@ There are a few functions specific to Python functions.
|
|||
|
||||
.. c:type:: PyFunction_WatchEvent
|
||||
|
||||
Enumeration of possible function watcher events:
|
||||
- ``PyFunction_EVENT_CREATE``
|
||||
- ``PyFunction_EVENT_DESTROY``
|
||||
- ``PyFunction_EVENT_MODIFY_CODE``
|
||||
- ``PyFunction_EVENT_MODIFY_DEFAULTS``
|
||||
- ``PyFunction_EVENT_MODIFY_KWDEFAULTS``
|
||||
Enumeration of possible function watcher events:
|
||||
|
||||
- ``PyFunction_EVENT_CREATE``
|
||||
- ``PyFunction_EVENT_DESTROY``
|
||||
- ``PyFunction_EVENT_MODIFY_CODE``
|
||||
- ``PyFunction_EVENT_MODIFY_DEFAULTS``
|
||||
- ``PyFunction_EVENT_MODIFY_KWDEFAULTS``
|
||||
|
||||
.. versionadded:: 3.12
|
||||
|
||||
|
|
|
|||
|
|
@ -57,11 +57,49 @@ rules:
|
|||
Analogous to :c:macro:`PyObject_New` but for container objects with the
|
||||
:c:macro:`Py_TPFLAGS_HAVE_GC` flag set.
|
||||
|
||||
Do not call this directly to allocate memory for an object; call the type's
|
||||
:c:member:`~PyTypeObject.tp_alloc` slot instead.
|
||||
|
||||
When populating a type's :c:member:`~PyTypeObject.tp_alloc` slot,
|
||||
:c:func:`PyType_GenericAlloc` is preferred over a custom function that
|
||||
simply calls this macro.
|
||||
|
||||
Memory allocated by this macro must be freed with
|
||||
:c:func:`PyObject_GC_Del` (usually called via the object's
|
||||
:c:member:`~PyTypeObject.tp_free` slot).
|
||||
|
||||
.. seealso::
|
||||
|
||||
* :c:func:`PyObject_GC_Del`
|
||||
* :c:macro:`PyObject_New`
|
||||
* :c:func:`PyType_GenericAlloc`
|
||||
* :c:member:`~PyTypeObject.tp_alloc`
|
||||
|
||||
|
||||
.. c:macro:: PyObject_GC_NewVar(TYPE, typeobj, size)
|
||||
|
||||
Analogous to :c:macro:`PyObject_NewVar` but for container objects with the
|
||||
:c:macro:`Py_TPFLAGS_HAVE_GC` flag set.
|
||||
|
||||
Do not call this directly to allocate memory for an object; call the type's
|
||||
:c:member:`~PyTypeObject.tp_alloc` slot instead.
|
||||
|
||||
When populating a type's :c:member:`~PyTypeObject.tp_alloc` slot,
|
||||
:c:func:`PyType_GenericAlloc` is preferred over a custom function that
|
||||
simply calls this macro.
|
||||
|
||||
Memory allocated by this macro must be freed with
|
||||
:c:func:`PyObject_GC_Del` (usually called via the object's
|
||||
:c:member:`~PyTypeObject.tp_free` slot).
|
||||
|
||||
.. seealso::
|
||||
|
||||
* :c:func:`PyObject_GC_Del`
|
||||
* :c:macro:`PyObject_NewVar`
|
||||
* :c:func:`PyType_GenericAlloc`
|
||||
* :c:member:`~PyTypeObject.tp_alloc`
|
||||
|
||||
|
||||
.. c:function:: PyObject* PyUnstable_Object_GC_NewWithExtraData(PyTypeObject *type, size_t extra_size)
|
||||
|
||||
Analogous to :c:macro:`PyObject_GC_New` but allocates *extra_size*
|
||||
|
|
@ -73,6 +111,10 @@ rules:
|
|||
The extra data will be deallocated with the object, but otherwise it is
|
||||
not managed by Python.
|
||||
|
||||
Memory allocated by this function must be freed with
|
||||
:c:func:`PyObject_GC_Del` (usually called via the object's
|
||||
:c:member:`~PyTypeObject.tp_free` slot).
|
||||
|
||||
.. warning::
|
||||
The function is marked as unstable because the final mechanism
|
||||
for reserving extra data after an instance is not yet decided.
|
||||
|
|
@ -136,6 +178,21 @@ rules:
|
|||
Releases memory allocated to an object using :c:macro:`PyObject_GC_New` or
|
||||
:c:macro:`PyObject_GC_NewVar`.
|
||||
|
||||
Do not call this directly to free an object's memory; call the type's
|
||||
:c:member:`~PyTypeObject.tp_free` slot instead.
|
||||
|
||||
Do not use this for memory allocated by :c:macro:`PyObject_New`,
|
||||
:c:macro:`PyObject_NewVar`, or related allocation functions; use
|
||||
:c:func:`PyObject_Free` instead.
|
||||
|
||||
.. seealso::
|
||||
|
||||
* :c:func:`PyObject_Free` is the non-GC equivalent of this function.
|
||||
* :c:macro:`PyObject_GC_New`
|
||||
* :c:macro:`PyObject_GC_NewVar`
|
||||
* :c:func:`PyType_GenericAlloc`
|
||||
* :c:member:`~PyTypeObject.tp_free`
|
||||
|
||||
|
||||
.. c:function:: void PyObject_GC_UnTrack(void *op)
|
||||
|
||||
|
|
@ -180,9 +237,9 @@ provided. In order to use this macro, the :c:member:`~PyTypeObject.tp_traverse`
|
|||
must name its arguments exactly *visit* and *arg*:
|
||||
|
||||
|
||||
.. c:function:: void Py_VISIT(PyObject *o)
|
||||
.. c:macro:: Py_VISIT(o)
|
||||
|
||||
If *o* is not ``NULL``, call the *visit* callback, with arguments *o*
|
||||
If the :c:expr:`PyObject *` *o* is not ``NULL``, call the *visit* callback, with arguments *o*
|
||||
and *arg*. If *visit* returns a non-zero value, then return it.
|
||||
Using this macro, :c:member:`~PyTypeObject.tp_traverse` handlers
|
||||
look like::
|
||||
|
|
@ -277,7 +334,7 @@ the garbage collector.
|
|||
|
||||
Type of the visitor function to be passed to :c:func:`PyUnstable_GC_VisitObjects`.
|
||||
*arg* is the same as the *arg* passed to ``PyUnstable_GC_VisitObjects``.
|
||||
Return ``0`` to continue iteration, return ``1`` to stop iteration. Other return
|
||||
Return ``1`` to continue iteration, return ``0`` to stop iteration. Other return
|
||||
values are reserved for now so behavior on returning anything else is undefined.
|
||||
|
||||
.. versionadded:: 3.12
|
||||
|
|
|
|||
|
|
@ -16,19 +16,6 @@ Importing Modules
|
|||
This is a wrapper around :c:func:`PyImport_Import()` which takes a
|
||||
:c:expr:`const char *` as an argument instead of a :c:expr:`PyObject *`.
|
||||
|
||||
.. c:function:: PyObject* PyImport_ImportModuleNoBlock(const char *name)
|
||||
|
||||
This function is a deprecated alias of :c:func:`PyImport_ImportModule`.
|
||||
|
||||
.. versionchanged:: 3.3
|
||||
This function used to fail immediately when the import lock was held
|
||||
by another thread. In Python 3.3 though, the locking scheme switched
|
||||
to per-module locks for most purposes, so this function's special
|
||||
behaviour isn't needed anymore.
|
||||
|
||||
.. deprecated-removed:: 3.13 3.15
|
||||
Use :c:func:`PyImport_ImportModule` instead.
|
||||
|
||||
|
||||
.. c:function:: PyObject* PyImport_ImportModuleEx(const char *name, PyObject *globals, PyObject *locals, PyObject *fromlist)
|
||||
|
||||
|
|
@ -325,3 +312,24 @@ Importing Modules
|
|||
If Python is initialized multiple times, :c:func:`PyImport_AppendInittab` or
|
||||
:c:func:`PyImport_ExtendInittab` must be called before each Python
|
||||
initialization.
|
||||
|
||||
|
||||
.. c:function:: PyObject* PyImport_ImportModuleAttr(PyObject *mod_name, PyObject *attr_name)
|
||||
|
||||
Import the module *mod_name* and get its attribute *attr_name*.
|
||||
|
||||
Names must be Python :class:`str` objects.
|
||||
|
||||
Helper function combining :c:func:`PyImport_Import` and
|
||||
:c:func:`PyObject_GetAttr`. For example, it can raise :exc:`ImportError` if
|
||||
the module is not found, and :exc:`AttributeError` if the attribute doesn't
|
||||
exist.
|
||||
|
||||
.. versionadded:: 3.14
|
||||
|
||||
.. c:function:: PyObject* PyImport_ImportModuleAttrString(const char *mod_name, const char *attr_name)
|
||||
|
||||
Similar to :c:func:`PyImport_ImportModuleAttr`, but names are UTF-8 encoded
|
||||
strings instead of Python :class:`str` objects.
|
||||
|
||||
.. versionadded:: 3.14
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -30,6 +30,16 @@ familiar with writing an extension before attempting to embed Python in a real
|
|||
application.
|
||||
|
||||
|
||||
Language version compatibility
|
||||
==============================
|
||||
|
||||
Python's C API is compatible with C11 and C++11 versions of C and C++.
|
||||
|
||||
This is a lower limit: the C API does not require features from later
|
||||
C/C++ versions.
|
||||
You do *not* need to enable your compiler's "c11 mode".
|
||||
|
||||
|
||||
Coding standards
|
||||
================
|
||||
|
||||
|
|
@ -138,7 +148,7 @@ complete listing.
|
|||
.. c:macro:: Py_ALWAYS_INLINE
|
||||
|
||||
Ask the compiler to always inline a static inline function. The compiler can
|
||||
ignore it and decides to not inline the function.
|
||||
ignore it and decide to not inline the function.
|
||||
|
||||
It can be used to inline performance critical static inline functions when
|
||||
building Python in debug mode with function inlining disabled. For example,
|
||||
|
|
@ -769,20 +779,11 @@ found along :envvar:`PATH`.) The user can override this behavior by setting the
|
|||
environment variable :envvar:`PYTHONHOME`, or insert additional directories in
|
||||
front of the standard path by setting :envvar:`PYTHONPATH`.
|
||||
|
||||
.. index::
|
||||
single: Py_GetPath (C function)
|
||||
single: Py_GetPrefix (C function)
|
||||
single: Py_GetExecPrefix (C function)
|
||||
single: Py_GetProgramFullPath (C function)
|
||||
|
||||
The embedding application can steer the search by setting
|
||||
:c:member:`PyConfig.program_name` *before* calling
|
||||
:c:func:`Py_InitializeFromConfig`. Note that
|
||||
:envvar:`PYTHONHOME` still overrides this and :envvar:`PYTHONPATH` is still
|
||||
inserted in front of the standard path. An application that requires total
|
||||
control has to provide its own implementation of :c:func:`Py_GetPath`,
|
||||
:c:func:`Py_GetPrefix`, :c:func:`Py_GetExecPrefix`, and
|
||||
:c:func:`Py_GetProgramFullPath` (all defined in :file:`Modules/getpath.c`).
|
||||
inserted in front of the standard path.
|
||||
|
||||
.. index:: single: Py_IsInitialized (C function)
|
||||
|
||||
|
|
@ -816,14 +817,17 @@ frequently used builds will be described in the remainder of this section.
|
|||
|
||||
Compiling the interpreter with the :c:macro:`!Py_DEBUG` macro defined produces
|
||||
what is generally meant by :ref:`a debug build of Python <debug-build>`.
|
||||
:c:macro:`!Py_DEBUG` is enabled in the Unix build by adding
|
||||
:option:`--with-pydebug` to the :file:`./configure` command.
|
||||
It is also implied by the presence of the
|
||||
not-Python-specific :c:macro:`!_DEBUG` macro. When :c:macro:`!Py_DEBUG` is enabled
|
||||
in the Unix build, compiler optimization is disabled.
|
||||
|
||||
On Unix, :c:macro:`!Py_DEBUG` can be enabled by adding :option:`--with-pydebug`
|
||||
to the :file:`./configure` command. This will also disable compiler optimization.
|
||||
|
||||
On Windows, selecting a debug build (e.g., by passing the :option:`-d` option to
|
||||
:file:`PCbuild/build.bat`) automatically enables :c:macro:`!Py_DEBUG`.
|
||||
Additionally, the presence of the not-Python-specific :c:macro:`!_DEBUG` macro,
|
||||
when defined by the compiler, will also implicitly enable :c:macro:`!Py_DEBUG`.
|
||||
|
||||
In addition to the reference count debugging described below, extra checks are
|
||||
performed, see :ref:`Python Debug Build <debug-build>`.
|
||||
performed. See :ref:`Python Debug Build <debug-build>` for more details.
|
||||
|
||||
Defining :c:macro:`Py_TRACE_REFS` enables reference tracing
|
||||
(see the :option:`configure --with-trace-refs option <--with-trace-refs>`).
|
||||
|
|
|
|||
156
Doc/c-api/lifecycle.dot
Normal file
156
Doc/c-api/lifecycle.dot
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
digraph "Life Events" {
|
||||
graph [
|
||||
fontnames="svg"
|
||||
fontsize=12.0
|
||||
id="life_events_graph"
|
||||
layout="dot"
|
||||
margin="0,0"
|
||||
ranksep=0.25
|
||||
stylesheet="lifecycle.dot.css"
|
||||
]
|
||||
node [
|
||||
fontname="Courier"
|
||||
fontsize=12.0
|
||||
]
|
||||
edge [
|
||||
fontname="Times-Italic"
|
||||
fontsize=12.0
|
||||
]
|
||||
|
||||
"start" [fontname="Times-Italic" shape=plain label=< start > style=invis]
|
||||
{
|
||||
rank="same"
|
||||
"tp_new" [href="typeobj.html#c.PyTypeObject.tp_new" target="_top"]
|
||||
"tp_alloc" [href="typeobj.html#c.PyTypeObject.tp_alloc" target="_top"]
|
||||
}
|
||||
"tp_init" [href="typeobj.html#c.PyTypeObject.tp_init" target="_top"]
|
||||
"reachable" [fontname="Times-Italic" shape=box]
|
||||
"tp_traverse" [
|
||||
href="typeobj.html#c.PyTypeObject.tp_traverse"
|
||||
ordering="in"
|
||||
target="_top"
|
||||
]
|
||||
"finalized?" [
|
||||
fontname="Times-Italic"
|
||||
label=<marked as<br/>finalized?>
|
||||
ordering="in"
|
||||
shape=diamond
|
||||
tooltip="marked as finalized?"
|
||||
]
|
||||
"tp_finalize" [
|
||||
href="typeobj.html#c.PyTypeObject.tp_finalize"
|
||||
ordering="in"
|
||||
target="_top"
|
||||
]
|
||||
"tp_clear" [href="typeobj.html#c.PyTypeObject.tp_clear" target="_top"]
|
||||
"uncollectable" [
|
||||
fontname="Times-Italic"
|
||||
label=<uncollectable<br/>(leaked)>
|
||||
shape=box
|
||||
tooltip="uncollectable (leaked)"
|
||||
]
|
||||
"tp_dealloc" [
|
||||
href="typeobj.html#c.PyTypeObject.tp_dealloc"
|
||||
ordering="in"
|
||||
target="_top"
|
||||
]
|
||||
"tp_free" [href="typeobj.html#c.PyTypeObject.tp_free" target="_top"]
|
||||
|
||||
"start" -> "tp_new" [
|
||||
label=< type call >
|
||||
]
|
||||
"tp_new" -> "tp_alloc" [
|
||||
label=< direct call > arrowhead=empty
|
||||
labeltooltip="tp_new to tp_alloc: direct call"
|
||||
tooltip="tp_new to tp_alloc: direct call"
|
||||
]
|
||||
"tp_new" -> "tp_init" [tooltip="tp_new to tp_init"]
|
||||
"tp_init" -> "reachable" [tooltip="tp_init to reachable"]
|
||||
"reachable" -> "tp_traverse" [
|
||||
dir="back"
|
||||
label=< not in a <br/> cyclic <br/> isolate >
|
||||
labeltooltip="tp_traverse to reachable: not in a cyclic isolate"
|
||||
tooltip="tp_traverse to reachable: not in a cyclic isolate"
|
||||
]
|
||||
"reachable" -> "tp_traverse" [
|
||||
label=< periodic <br/> cyclic isolate <br/> detection >
|
||||
labeltooltip="reachable to tp_traverse: periodic cyclic isolate detection"
|
||||
tooltip="reachable to tp_traverse: periodic cyclic isolate detection"
|
||||
]
|
||||
"reachable" -> "tp_init" [tooltip="reachable to tp_init"]
|
||||
"reachable" -> "tp_finalize" [
|
||||
dir="back"
|
||||
label=< resurrected <br/> (maybe remove <br/> finalized mark) >
|
||||
labeltooltip="tp_finalize to reachable: resurrected (maybe remove finalized mark)"
|
||||
tooltip="tp_finalize to reachable: resurrected (maybe remove finalized mark)"
|
||||
]
|
||||
"tp_traverse" -> "finalized?" [
|
||||
label=< cyclic <br/> isolate >
|
||||
labeltooltip="tp_traverse to finalized?: cyclic isolate"
|
||||
tooltip="tp_traverse to finalized?: cyclic isolate"
|
||||
]
|
||||
"reachable" -> "finalized?" [
|
||||
label=< no refs >
|
||||
labeltooltip="reachable to finalized?: no refs"
|
||||
tooltip="reachable to finalized?: no refs"
|
||||
]
|
||||
"finalized?" -> "tp_finalize" [
|
||||
label=< no (mark <br/> as finalized) >
|
||||
labeltooltip="finalized? to tp_finalize: no (mark as finalized)"
|
||||
tooltip="finalized? to tp_finalize: no (mark as finalized)"
|
||||
]
|
||||
"finalized?" -> "tp_clear" [
|
||||
label=< yes >
|
||||
labeltooltip="finalized? to tp_clear: yes"
|
||||
tooltip="finalized? to tp_clear: yes"
|
||||
]
|
||||
"tp_finalize" -> "tp_clear" [
|
||||
label=< no refs or <br/> cyclic isolate >
|
||||
labeltooltip="tp_finalize to tp_clear: no refs or cyclic isolate"
|
||||
tooltip="tp_finalize to tp_clear: no refs or cyclic isolate"
|
||||
]
|
||||
"tp_finalize" -> "tp_dealloc" [
|
||||
arrowtail=empty
|
||||
dir="back"
|
||||
href="lifecycle.html#c.PyObject_CallFinalizerFromDealloc"
|
||||
style=dashed
|
||||
label=< recommended<br/> call (see<br/> explanation)>
|
||||
labeltooltip="tp_dealloc to tp_finalize: recommended call (see explanation)"
|
||||
target="_top"
|
||||
tooltip="tp_dealloc to tp_finalize: recommended call (see explanation)"
|
||||
]
|
||||
"tp_finalize" -> "tp_dealloc" [
|
||||
label=< no refs >
|
||||
labeltooltip="tp_finalize to tp_dealloc: no refs"
|
||||
tooltip="tp_finalize to tp_dealloc: no refs"
|
||||
]
|
||||
"tp_clear" -> "tp_dealloc" [
|
||||
label=< no refs >
|
||||
labeltooltip="tp_clear to tp_dealloc: no refs"
|
||||
tooltip="tp_clear to tp_dealloc: no refs"
|
||||
]
|
||||
"tp_clear" -> "uncollectable" [
|
||||
label=< cyclic <br/> isolate >
|
||||
labeltooltip="tp_clear to uncollectable: cyclic isolate"
|
||||
tooltip="tp_clear to uncollectable: cyclic isolate"
|
||||
]
|
||||
"uncollectable" -> "tp_dealloc" [
|
||||
style=invis
|
||||
tooltip="uncollectable to tp_dealloc"
|
||||
]
|
||||
"reachable" -> "uncollectable" [
|
||||
label=< cyclic <br/> isolate <br/> (no GC <br/> support) >
|
||||
labeltooltip="reachable to uncollectable: cyclic isolate (no GC support)"
|
||||
tooltip="reachable to uncollectable: cyclic isolate (no GC support)"
|
||||
]
|
||||
"reachable" -> "tp_dealloc" [
|
||||
label=< no refs>
|
||||
labeltooltip="reachable to tp_dealloc: no refs"
|
||||
]
|
||||
"tp_dealloc" -> "tp_free" [
|
||||
arrowhead=empty
|
||||
label=< direct call >
|
||||
labeltooltip="tp_dealloc to tp_free: direct call"
|
||||
tooltip="tp_dealloc to tp_free: direct call"
|
||||
]
|
||||
}
|
||||
21
Doc/c-api/lifecycle.dot.css
Normal file
21
Doc/c-api/lifecycle.dot.css
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#life_events_graph {
|
||||
--svg-fgcolor: currentcolor;
|
||||
--svg-bgcolor: transparent;
|
||||
}
|
||||
#life_events_graph a {
|
||||
color: inherit;
|
||||
}
|
||||
#life_events_graph [stroke="black"] {
|
||||
stroke: var(--svg-fgcolor);
|
||||
}
|
||||
#life_events_graph text,
|
||||
#life_events_graph [fill="black"] {
|
||||
fill: var(--svg-fgcolor);
|
||||
}
|
||||
#life_events_graph [fill="white"] {
|
||||
fill: var(--svg-bgcolor);
|
||||
}
|
||||
#life_events_graph [fill="none"] {
|
||||
/* On links, setting fill will make the entire shape clickable */
|
||||
fill: var(--svg-bgcolor);
|
||||
}
|
||||
BIN
Doc/c-api/lifecycle.dot.pdf
Normal file
BIN
Doc/c-api/lifecycle.dot.pdf
Normal file
Binary file not shown.
374
Doc/c-api/lifecycle.dot.svg
generated
Normal file
374
Doc/c-api/lifecycle.dot.svg
generated
Normal file
|
|
@ -0,0 +1,374 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<?xml-stylesheet href="lifecycle.dot.css" type="text/css"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<!-- Generated by graphviz version 12.2.0 (0)
|
||||
-->
|
||||
<!-- Title: Life Events Pages: 1 -->
|
||||
<svg width="465pt" height="845pt"
|
||||
viewBox="0.00 0.00 465.30 845.00" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||
<g id="life_events_graph" class="graph" transform="scale(1 1) rotate(0) translate(4 841)">
|
||||
<title>Life Events</title>
|
||||
<polygon fill="white" stroke="none" points="-4,4 -4,-841 461.3,-841 461.3,4 -4,4"/>
|
||||
<!-- start -->
|
||||
<!-- tp_new -->
|
||||
<g id="life_events_graph_node2" class="node">
|
||||
<title>tp_new</title>
|
||||
<g id="a_life_events_graph_node2"><a xlink:href="typeobj.html#c.PyTypeObject.tp_new" xlink:title="tp_new" target="_top">
|
||||
<ellipse fill="none" stroke="black" cx="192.8" cy="-772.5" rx="38.8" ry="18"/>
|
||||
<text text-anchor="middle" x="192.8" y="-768.23" font-family="monospace,monospace" font-size="12.00">tp_new</text>
|
||||
</a>
|
||||
</g>
|
||||
</g>
|
||||
<!-- start->tp_new -->
|
||||
<g id="life_events_graph_edge1" class="edge">
|
||||
<title>start->tp_new</title>
|
||||
<g id="a_life_events_graph_edge1"><a xlink:title="start to tp_new: type call">
|
||||
<path fill="none" stroke="black" d="M192.8,-822.95C192.8,-817.85 192.8,-810.09 192.8,-802.22"/>
|
||||
<polygon fill="black" stroke="black" points="196.3,-802.42 192.8,-792.42 189.3,-802.42 196.3,-802.42"/>
|
||||
</a>
|
||||
</g>
|
||||
<g id="a_life_events_graph_edge1-label"><a xlink:title="start to tp_new: type call">
|
||||
<text text-anchor="start" x="192.8" y="-802.35" font-family="serif,serif" font-style="italic" font-size="12.00">    type call  </text>
|
||||
</a>
|
||||
</g>
|
||||
</g>
|
||||
<!-- tp_alloc -->
|
||||
<g id="life_events_graph_node3" class="node">
|
||||
<title>tp_alloc</title>
|
||||
<g id="a_life_events_graph_node3"><a xlink:href="typeobj.html#c.PyTypeObject.tp_alloc" xlink:title="tp_alloc" target="_top">
|
||||
<ellipse fill="none" stroke="black" cx="373.8" cy="-772.5" rx="48.34" ry="18"/>
|
||||
<text text-anchor="middle" x="373.8" y="-768.23" font-family="monospace,monospace" font-size="12.00">tp_alloc</text>
|
||||
</a>
|
||||
</g>
|
||||
</g>
|
||||
<!-- tp_new->tp_alloc -->
|
||||
<g id="life_events_graph_edge2" class="edge">
|
||||
<title>tp_new->tp_alloc</title>
|
||||
<g id="a_life_events_graph_edge2"><a xlink:title="tp_new to tp_alloc: direct call">
|
||||
<path fill="none" stroke="black" d="M232.07,-772.5C256,-772.5 287.05,-772.5 313.98,-772.5"/>
|
||||
<polygon fill="none" stroke="black" points="313.73,-776 323.73,-772.5 313.73,-769 313.73,-776"/>
|
||||
</a>
|
||||
</g>
|
||||
<g id="a_life_events_graph_edge2-label"><a xlink:title="tp_new to tp_alloc: direct call">
|
||||
<text text-anchor="start" x="240.65" y="-778.35" font-family="serif,serif" font-style="italic" font-size="12.00">  direct call  </text>
|
||||
</a>
|
||||
</g>
|
||||
</g>
|
||||
<!-- tp_init -->
|
||||
<g id="life_events_graph_node4" class="node">
|
||||
<title>tp_init</title>
|
||||
<g id="a_life_events_graph_node4"><a xlink:href="typeobj.html#c.PyTypeObject.tp_init" xlink:title="tp_init" target="_top">
|
||||
<ellipse fill="none" stroke="black" cx="192.8" cy="-717.5" rx="43.57" ry="18"/>
|
||||
<text text-anchor="middle" x="192.8" y="-713.23" font-family="monospace,monospace" font-size="12.00">tp_init</text>
|
||||
</a>
|
||||
</g>
|
||||
</g>
|
||||
<!-- tp_new->tp_init -->
|
||||
<g id="life_events_graph_edge3" class="edge">
|
||||
<title>tp_new->tp_init</title>
|
||||
<g id="a_life_events_graph_edge3"><a xlink:title="tp_new to tp_init">
|
||||
<path fill="none" stroke="black" d="M192.8,-754.15C192.8,-751.83 192.8,-749.42 192.8,-746.98"/>
|
||||
<polygon fill="black" stroke="black" points="196.3,-747.23 192.8,-737.23 189.3,-747.23 196.3,-747.23"/>
|
||||
</a>
|
||||
</g>
|
||||
</g>
|
||||
<!-- reachable -->
|
||||
<g id="life_events_graph_node5" class="node">
|
||||
<title>reachable</title>
|
||||
<polygon fill="none" stroke="black" points="230.8,-680.5 154.8,-680.5 154.8,-644.5 230.8,-644.5 230.8,-680.5"/>
|
||||
<text text-anchor="middle" x="192.8" y="-658.23" font-family="serif,serif" font-style="italic" font-size="12.00">reachable</text>
|
||||
</g>
|
||||
<!-- tp_init->reachable -->
|
||||
<g id="life_events_graph_edge4" class="edge">
|
||||
<title>tp_init->reachable</title>
|
||||
<g id="a_life_events_graph_edge4"><a xlink:title="tp_init to reachable">
|
||||
<path fill="none" stroke="black" d="M186.44,-699.44C186.24,-697.12 186.11,-694.69 186.07,-692.24"/>
|
||||
<polygon fill="black" stroke="black" points="189.56,-692.51 186.37,-682.41 182.56,-692.29 189.56,-692.51"/>
|
||||
</a>
|
||||
</g>
|
||||
</g>
|
||||
<!-- reachable->tp_init -->
|
||||
<g id="life_events_graph_edge7" class="edge">
|
||||
<title>reachable->tp_init</title>
|
||||
<g id="a_life_events_graph_edge7"><a xlink:title="reachable to tp_init">
|
||||
<path fill="none" stroke="black" d="M199.18,-680.89C199.37,-683.22 199.49,-685.65 199.53,-688.11"/>
|
||||
<polygon fill="black" stroke="black" points="196.04,-687.81 199.2,-697.93 203.04,-688.05 196.04,-687.81"/>
|
||||
</a>
|
||||
</g>
|
||||
</g>
|
||||
<!-- tp_traverse -->
|
||||
<g id="life_events_graph_node6" class="node">
|
||||
<title>tp_traverse</title>
|
||||
<g id="a_life_events_graph_node6"><a xlink:href="typeobj.html#c.PyTypeObject.tp_traverse" xlink:title="tp_traverse" target="_top">
|
||||
<ellipse fill="none" stroke="black" cx="136.8" cy="-565.75" rx="62.65" ry="18"/>
|
||||
<text text-anchor="middle" x="136.8" y="-561.48" font-family="monospace,monospace" font-size="12.00">tp_traverse</text>
|
||||
</a>
|
||||
</g>
|
||||
</g>
|
||||
<!-- reachable->tp_traverse -->
|
||||
<g id="life_events_graph_edge5" class="edge">
|
||||
<title>reachable->tp_traverse</title>
|
||||
<g id="a_life_events_graph_edge5"><a xlink:title="tp_traverse to reachable: not in a cyclic isolate">
|
||||
<path fill="none" stroke="black" d="M143.43,-658.77C108.3,-655.68 65.38,-649.16 54.05,-635.5 41.91,-620.88 42.8,-608.07 54.05,-592.75 60.55,-583.89 70.07,-577.97 80.37,-574.03"/>
|
||||
<polygon fill="black" stroke="black" points="142.76,-662.23 153.01,-659.54 143.32,-655.25 142.76,-662.23"/>
|
||||
</a>
|
||||
</g>
|
||||
<g id="a_life_events_graph_edge5-label"><a xlink:title="tp_traverse to reachable: not in a cyclic isolate">
|
||||
<text text-anchor="start" x="54.05" y="-624.1" font-family="serif,serif" font-style="italic" font-size="12.00">  not in a  </text>
|
||||
<text text-anchor="start" x="59.67" y="-609.85" font-family="serif,serif" font-style="italic" font-size="12.00">  cyclic  </text>
|
||||
<text text-anchor="start" x="57.05" y="-595.6" font-family="serif,serif" font-style="italic" font-size="12.00">  isolate  </text>
|
||||
</a>
|
||||
</g>
|
||||
</g>
|
||||
<!-- reachable->tp_traverse -->
|
||||
<g id="life_events_graph_edge6" class="edge">
|
||||
<title>reachable->tp_traverse</title>
|
||||
<g id="a_life_events_graph_edge6"><a xlink:title="reachable to tp_traverse: periodic cyclic isolate detection">
|
||||
<path fill="none" stroke="black" d="M154.41,-650.07C147.94,-646.44 142.04,-641.69 138.05,-635.5 130.52,-623.82 129.57,-608.56 130.79,-595.38"/>
|
||||
<polygon fill="black" stroke="black" points="134.25,-595.91 132.17,-585.52 127.31,-594.94 134.25,-595.91"/>
|
||||
</a>
|
||||
</g>
|
||||
<g id="a_life_events_graph_edge6-label"><a xlink:title="reachable to tp_traverse: periodic cyclic isolate detection">
|
||||
<text text-anchor="start" x="154.17" y="-624.1" font-family="serif,serif" font-style="italic" font-size="12.00">  periodic  </text>
|
||||
<text text-anchor="start" x="138.05" y="-609.85" font-family="serif,serif" font-style="italic" font-size="12.00">  cyclic isolate   </text>
|
||||
<text text-anchor="start" x="151.17" y="-595.6" font-family="serif,serif" font-style="italic" font-size="12.00">  detection  </text>
|
||||
</a>
|
||||
</g>
|
||||
</g>
|
||||
<!-- finalized? -->
|
||||
<g id="life_events_graph_node7" class="node">
|
||||
<title>finalized?</title>
|
||||
<g id="a_life_events_graph_node7"><a xlink:title="marked as finalized?">
|
||||
<polygon fill="none" stroke="black" points="191.8,-487 112.05,-450.5 191.8,-414 271.55,-450.5 191.8,-487"/>
|
||||
<text text-anchor="start" x="159.92" y="-453.35" font-family="serif,serif" font-style="italic" font-size="12.00">marked as</text>
|
||||
<text text-anchor="start" x="162.92" y="-439.1" font-family="serif,serif" font-style="italic" font-size="12.00">finalized?</text>
|
||||
</a>
|
||||
</g>
|
||||
</g>
|
||||
<!-- reachable->finalized? -->
|
||||
<g id="life_events_graph_edge10" class="edge">
|
||||
<title>reachable->finalized?</title>
|
||||
<g id="a_life_events_graph_edge10"><a xlink:title="reachable to finalized?: no refs">
|
||||
<path fill="none" stroke="black" d="M227.72,-644.32C230.51,-641.73 232.96,-638.8 234.8,-635.5 244.04,-618.9 235.48,-611.74 234.8,-592.75 233.24,-549.67 243.64,-536.1 227.8,-496 226.37,-492.38 224.53,-488.82 222.45,-485.4"/>
|
||||
<polygon fill="black" stroke="black" points="225.47,-483.62 216.91,-477.39 219.72,-487.61 225.47,-483.62"/>
|
||||
</a>
|
||||
</g>
|
||||
<g id="a_life_events_graph_edge10-label"><a xlink:title="reachable to finalized?: no refs">
|
||||
<text text-anchor="start" x="236.45" y="-561.48" font-family="serif,serif" font-style="italic" font-size="12.00">  no refs  </text>
|
||||
</a>
|
||||
</g>
|
||||
</g>
|
||||
<!-- tp_finalize -->
|
||||
<g id="life_events_graph_node8" class="node">
|
||||
<title>tp_finalize</title>
|
||||
<g id="a_life_events_graph_node8"><a xlink:href="typeobj.html#c.PyTypeObject.tp_finalize" xlink:title="tp_finalize" target="_top">
|
||||
<ellipse fill="none" stroke="black" cx="122.8" cy="-321" rx="62.65" ry="18"/>
|
||||
<text text-anchor="middle" x="122.8" y="-316.73" font-family="monospace,monospace" font-size="12.00">tp_finalize</text>
|
||||
</a>
|
||||
</g>
|
||||
</g>
|
||||
<!-- reachable->tp_finalize -->
|
||||
<g id="life_events_graph_edge8" class="edge">
|
||||
<title>reachable->tp_finalize</title>
|
||||
<g id="a_life_events_graph_edge8"><a xlink:title="tp_finalize to reachable: resurrected (maybe remove finalized mark)">
|
||||
<path fill="none" stroke="black" d="M142.86,-659.6C103.8,-656.96 53.97,-650.63 40.8,-635.5 -37.32,-545.75 69.61,-390.31 109.14,-338.99"/>
|
||||
<polygon fill="black" stroke="black" points="142.62,-663.09 152.82,-660.2 143.05,-656.11 142.62,-663.09"/>
|
||||
</a>
|
||||
</g>
|
||||
<g id="a_life_events_graph_edge8-label"><a xlink:title="tp_finalize to reachable: resurrected (maybe remove finalized mark)">
|
||||
<text text-anchor="start" x="33.68" y="-527.35" font-family="serif,serif" font-style="italic" font-size="12.00">  resurrected  </text>
|
||||
<text text-anchor="start" x="22.43" y="-513.1" font-family="serif,serif" font-style="italic" font-size="12.00">  (maybe remove  </text>
|
||||
<text text-anchor="start" x="23.18" y="-498.85" font-family="serif,serif" font-style="italic" font-size="12.00">  finalized mark)  </text>
|
||||
</a>
|
||||
</g>
|
||||
</g>
|
||||
<!-- uncollectable -->
|
||||
<g id="life_events_graph_node10" class="node">
|
||||
<title>uncollectable</title>
|
||||
<g id="a_life_events_graph_node10"><a xlink:title="uncollectable (leaked)">
|
||||
<polygon fill="none" stroke="black" points="371.92,-159.75 275.67,-159.75 275.67,-123.25 371.92,-123.25 371.92,-159.75"/>
|
||||
<text text-anchor="start" x="283.67" y="-144.35" font-family="serif,serif" font-style="italic" font-size="12.00">uncollectable</text>
|
||||
<text text-anchor="start" x="299.42" y="-130.1" font-family="serif,serif" font-style="italic" font-size="12.00">(leaked)</text>
|
||||
</a>
|
||||
</g>
|
||||
</g>
|
||||
<!-- reachable->uncollectable -->
|
||||
<g id="life_events_graph_edge19" class="edge">
|
||||
<title>reachable->uncollectable</title>
|
||||
<g id="a_life_events_graph_edge19"><a xlink:title="reachable to uncollectable: cyclic isolate (no GC support)">
|
||||
<path fill="none" stroke="black" d="M231.2,-652.03C270.79,-639.69 326.8,-613.9 326.8,-566.75 326.8,-566.75 326.8,-566.75 326.8,-237.5 326.8,-215.3 325.97,-190.2 325.18,-171.37"/>
|
||||
<polygon fill="black" stroke="black" points="328.68,-171.35 324.75,-161.52 321.69,-171.66 328.68,-171.35"/>
|
||||
</a>
|
||||
</g>
|
||||
<g id="a_life_events_graph_edge19-label"><a xlink:title="reachable to uncollectable: cyclic isolate (no GC support)">
|
||||
<text text-anchor="start" x="335.05" y="-393.6" font-family="serif,serif" font-style="italic" font-size="12.00">  cyclic  </text>
|
||||
<text text-anchor="start" x="332.42" y="-379.35" font-family="serif,serif" font-style="italic" font-size="12.00">  isolate  </text>
|
||||
<text text-anchor="start" x="331.3" y="-365.1" font-family="serif,serif" font-style="italic" font-size="12.00">  (no GC  </text>
|
||||
<text text-anchor="start" x="326.8" y="-350.85" font-family="serif,serif" font-style="italic" font-size="12.00">  support)  </text>
|
||||
</a>
|
||||
</g>
|
||||
</g>
|
||||
<!-- tp_dealloc -->
|
||||
<g id="life_events_graph_node11" class="node">
|
||||
<title>tp_dealloc</title>
|
||||
<g id="a_life_events_graph_node11"><a xlink:href="typeobj.html#c.PyTypeObject.tp_dealloc" xlink:title="tp_dealloc" target="_top">
|
||||
<ellipse fill="none" stroke="black" cx="200.8" cy="-86.25" rx="57.88" ry="18"/>
|
||||
<text text-anchor="middle" x="200.8" y="-81.97" font-family="monospace,monospace" font-size="12.00">tp_dealloc</text>
|
||||
</a>
|
||||
</g>
|
||||
</g>
|
||||
<!-- reachable->tp_dealloc -->
|
||||
<g id="life_events_graph_edge20" class="edge">
|
||||
<title>reachable->tp_dealloc</title>
|
||||
<path fill="none" stroke="black" d="M231.23,-661.18C293.08,-658.43 407.8,-643.03 407.8,-566.75 407.8,-566.75 407.8,-566.75 407.8,-140.5 407.8,-111.22 329.12,-97.8 268.77,-91.82"/>
|
||||
<polygon fill="black" stroke="black" points="269.15,-88.34 258.87,-90.89 268.5,-95.31 269.15,-88.34"/>
|
||||
<g id="a_life_events_graph_edge20-label"><a xlink:title="reachable to tp_dealloc: no refs">
|
||||
<text text-anchor="start" x="407.8" y="-316.73" font-family="serif,serif" font-style="italic" font-size="12.00">  no refs</text>
|
||||
</a>
|
||||
</g>
|
||||
</g>
|
||||
<!-- tp_traverse->finalized? -->
|
||||
<g id="life_events_graph_edge9" class="edge">
|
||||
<title>tp_traverse->finalized?</title>
|
||||
<g id="a_life_events_graph_edge9"><a xlink:title="tp_traverse to finalized?: cyclic isolate">
|
||||
<path fill="none" stroke="black" d="M145.15,-547.55C152.4,-532.62 163.18,-510.43 172.55,-491.13"/>
|
||||
<polygon fill="black" stroke="black" points="175.7,-492.66 176.92,-482.14 169.4,-489.61 175.7,-492.66"/>
|
||||
</a>
|
||||
</g>
|
||||
<g id="a_life_events_graph_edge9-label"><a xlink:title="tp_traverse to finalized?: cyclic isolate">
|
||||
<text text-anchor="start" x="171.85" y="-520.23" font-family="serif,serif" font-style="italic" font-size="12.00">  cyclic  </text>
|
||||
<text text-anchor="start" x="169.22" y="-505.98" font-family="serif,serif" font-style="italic" font-size="12.00">  isolate  </text>
|
||||
</a>
|
||||
</g>
|
||||
</g>
|
||||
<!-- finalized?->tp_finalize -->
|
||||
<g id="life_events_graph_edge11" class="edge">
|
||||
<title>finalized?->tp_finalize</title>
|
||||
<g id="a_life_events_graph_edge11"><a xlink:title="finalized? to tp_finalize: no (mark as finalized)">
|
||||
<path fill="none" stroke="black" d="M172.89,-422.6C169.14,-416.89 165.34,-410.82 162.05,-405 151.89,-387.08 141.99,-366.11 134.68,-349.73"/>
|
||||
<polygon fill="black" stroke="black" points="137.89,-348.35 130.66,-340.61 131.48,-351.17 137.89,-348.35"/>
|
||||
</a>
|
||||
</g>
|
||||
<g id="a_life_events_graph_edge11-label"><a xlink:title="finalized? to tp_finalize: no (mark as finalized)">
|
||||
<text text-anchor="start" x="170.67" y="-379.35" font-family="serif,serif" font-style="italic" font-size="12.00">  no (mark  </text>
|
||||
<text text-anchor="start" x="162.05" y="-365.1" font-family="serif,serif" font-style="italic" font-size="12.00">  as finalized)  </text>
|
||||
</a>
|
||||
</g>
|
||||
</g>
|
||||
<!-- tp_clear -->
|
||||
<g id="life_events_graph_node9" class="node">
|
||||
<title>tp_clear</title>
|
||||
<g id="a_life_events_graph_node9"><a xlink:href="typeobj.html#c.PyTypeObject.tp_clear" xlink:title="tp_clear" target="_top">
|
||||
<ellipse fill="none" stroke="black" cx="222.8" cy="-238.5" rx="48.34" ry="18"/>
|
||||
<text text-anchor="middle" x="222.8" y="-234.22" font-family="monospace,monospace" font-size="12.00">tp_clear</text>
|
||||
</a>
|
||||
</g>
|
||||
</g>
|
||||
<!-- finalized?->tp_clear -->
|
||||
<g id="life_events_graph_edge12" class="edge">
|
||||
<title>finalized?->tp_clear</title>
|
||||
<g id="a_life_events_graph_edge12"><a xlink:title="finalized? to tp_clear: yes">
|
||||
<path fill="none" stroke="black" d="M227.56,-430.1C236.46,-423.41 244.86,-415.02 249.8,-405 277.22,-349.39 274.06,-322.55 249.8,-265.5 249.7,-265.27 249.6,-265.04 249.49,-264.81"/>
|
||||
<polygon fill="black" stroke="black" points="252.41,-262.88 243.93,-256.52 246.6,-266.78 252.41,-262.88"/>
|
||||
</a>
|
||||
</g>
|
||||
<g id="a_life_events_graph_edge12-label"><a xlink:title="finalized? to tp_clear: yes">
|
||||
<text text-anchor="start" x="269.2" y="-316.73" font-family="serif,serif" font-style="italic" font-size="12.00">  yes  </text>
|
||||
</a>
|
||||
</g>
|
||||
</g>
|
||||
<!-- tp_finalize->tp_clear -->
|
||||
<g id="life_events_graph_edge13" class="edge">
|
||||
<title>tp_finalize->tp_clear</title>
|
||||
<g id="a_life_events_graph_edge13"><a xlink:title="tp_finalize to tp_clear: no refs or cyclic isolate">
|
||||
<path fill="none" stroke="black" d="M130.02,-302.72C135.75,-290.85 144.8,-275.49 156.8,-265.5 161.95,-261.21 167.9,-257.57 174.07,-254.49"/>
|
||||
<polygon fill="black" stroke="black" points="175.46,-257.71 183.18,-250.45 172.62,-251.31 175.46,-257.71"/>
|
||||
</a>
|
||||
</g>
|
||||
<g id="a_life_events_graph_edge13-label"><a xlink:title="tp_finalize to tp_clear: no refs or cyclic isolate">
|
||||
<text text-anchor="start" x="164.3" y="-282.6" font-family="serif,serif" font-style="italic" font-size="12.00">  no refs or   </text>
|
||||
<text text-anchor="start" x="156.8" y="-268.35" font-family="serif,serif" font-style="italic" font-size="12.00">  cyclic isolate  </text>
|
||||
</a>
|
||||
</g>
|
||||
</g>
|
||||
<!-- tp_finalize->tp_dealloc -->
|
||||
<g id="life_events_graph_edge14" class="edge">
|
||||
<title>tp_finalize->tp_dealloc</title>
|
||||
<g id="a_life_events_graph_edge14"><a xlink:href="lifecycle.html#c.PyObject_CallFinalizerFromDealloc" xlink:title="tp_dealloc to tp_finalize: recommended call (see explanation)" target="_top">
|
||||
<path fill="none" stroke="black" stroke-dasharray="5,2" d="M85.85,-298.52C42.09,-270.18 -21.4,-218.11 7.8,-168.75 36.22,-120.7 99.95,-100.97 146.42,-92.87"/>
|
||||
<polygon fill="none" stroke="black" points="83.78,-301.35 94.11,-303.72 87.52,-295.43 83.78,-301.35"/>
|
||||
</a>
|
||||
</g>
|
||||
<g id="a_life_events_graph_edge14-label"><a xlink:href="lifecycle.html#c.PyObject_CallFinalizerFromDealloc" xlink:title="tp_dealloc to tp_finalize: recommended call (see explanation)" target="_top">
|
||||
<text text-anchor="start" x="7.8" y="-200.1" font-family="serif,serif" font-style="italic" font-size="12.00">  recommended</text>
|
||||
<text text-anchor="start" x="25.8" y="-185.85" font-family="serif,serif" font-style="italic" font-size="12.00">  call (see</text>
|
||||
<text text-anchor="start" x="13.05" y="-171.6" font-family="serif,serif" font-style="italic" font-size="12.00">  explanation)</text>
|
||||
</a>
|
||||
</g>
|
||||
</g>
|
||||
<!-- tp_finalize->tp_dealloc -->
|
||||
<g id="life_events_graph_edge15" class="edge">
|
||||
<title>tp_finalize->tp_dealloc</title>
|
||||
<g id="a_life_events_graph_edge15"><a xlink:title="tp_finalize to tp_dealloc: no refs">
|
||||
<path fill="none" stroke="black" d="M123.03,-302.58C123.95,-273.77 128.08,-214.78 146.05,-168.75 153.95,-148.5 167.56,-128.2 179.24,-112.92"/>
|
||||
<polygon fill="black" stroke="black" points="181.81,-115.32 185.25,-105.3 176.31,-110.98 181.81,-115.32"/>
|
||||
</a>
|
||||
</g>
|
||||
<g id="a_life_events_graph_edge15-label"><a xlink:title="tp_finalize to tp_dealloc: no refs">
|
||||
<text text-anchor="start" x="146.05" y="-185.85" font-family="serif,serif" font-style="italic" font-size="12.00">   no refs  </text>
|
||||
</a>
|
||||
</g>
|
||||
</g>
|
||||
<!-- tp_clear->uncollectable -->
|
||||
<g id="life_events_graph_edge17" class="edge">
|
||||
<title>tp_clear->uncollectable</title>
|
||||
<g id="a_life_events_graph_edge17"><a xlink:title="tp_clear to uncollectable: cyclic isolate">
|
||||
<path fill="none" stroke="black" d="M227.75,-220.38C232.99,-205 242.67,-182.74 258.05,-168.75 260.43,-166.58 263.02,-164.58 265.74,-162.73"/>
|
||||
<polygon fill="black" stroke="black" points="267.27,-165.89 274.12,-157.81 263.73,-159.86 267.27,-165.89"/>
|
||||
</a>
|
||||
</g>
|
||||
<g id="a_life_events_graph_edge17-label"><a xlink:title="tp_clear to uncollectable: cyclic isolate">
|
||||
<text text-anchor="start" x="260.67" y="-192.97" font-family="serif,serif" font-style="italic" font-size="12.00">  cyclic  </text>
|
||||
<text text-anchor="start" x="258.05" y="-178.72" font-family="serif,serif" font-style="italic" font-size="12.00">  isolate  </text>
|
||||
</a>
|
||||
</g>
|
||||
</g>
|
||||
<!-- tp_clear->tp_dealloc -->
|
||||
<g id="life_events_graph_edge16" class="edge">
|
||||
<title>tp_clear->tp_dealloc</title>
|
||||
<g id="a_life_events_graph_edge16"><a xlink:title="tp_clear to tp_dealloc: no refs">
|
||||
<path fill="none" stroke="black" d="M219.7,-220.24C216.92,-204.51 212.83,-180.61 209.8,-159.75 207.7,-145.34 205.67,-129.26 204.07,-115.92"/>
|
||||
<polygon fill="black" stroke="black" points="207.56,-115.59 202.91,-106.07 200.61,-116.41 207.56,-115.59"/>
|
||||
</a>
|
||||
</g>
|
||||
<g id="a_life_events_graph_edge16-label"><a xlink:title="tp_clear to tp_dealloc: no refs">
|
||||
<text text-anchor="start" x="209.8" y="-137.22" font-family="serif,serif" font-style="italic" font-size="12.00">  no refs  </text>
|
||||
</a>
|
||||
</g>
|
||||
</g>
|
||||
<!-- uncollectable->tp_dealloc -->
|
||||
<!-- tp_free -->
|
||||
<g id="life_events_graph_node12" class="node">
|
||||
<title>tp_free</title>
|
||||
<g id="a_life_events_graph_node12"><a xlink:href="typeobj.html#c.PyTypeObject.tp_free" xlink:title="tp_free" target="_top">
|
||||
<ellipse fill="none" stroke="black" cx="200.8" cy="-18" rx="43.57" ry="18"/>
|
||||
<text text-anchor="middle" x="200.8" y="-13.72" font-family="monospace,monospace" font-size="12.00">tp_free</text>
|
||||
</a>
|
||||
</g>
|
||||
</g>
|
||||
<!-- tp_dealloc->tp_free -->
|
||||
<g id="life_events_graph_edge21" class="edge">
|
||||
<title>tp_dealloc->tp_free</title>
|
||||
<g id="a_life_events_graph_edge21"><a xlink:title="tp_dealloc to tp_free: direct call">
|
||||
<path fill="none" stroke="black" d="M200.8,-67.84C200.8,-61.63 200.8,-54.46 200.8,-47.56"/>
|
||||
<polygon fill="none" stroke="black" points="204.3,-47.57 200.8,-37.57 197.3,-47.57 204.3,-47.57"/>
|
||||
</a>
|
||||
</g>
|
||||
<g id="a_life_events_graph_edge21-label"><a xlink:title="tp_dealloc to tp_free: direct call">
|
||||
<text text-anchor="start" x="200.8" y="-47.85" font-family="serif,serif" font-style="italic" font-size="12.00">    direct call  </text>
|
||||
</a>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 21 KiB |
273
Doc/c-api/lifecycle.rst
Normal file
273
Doc/c-api/lifecycle.rst
Normal file
|
|
@ -0,0 +1,273 @@
|
|||
.. highlight:: c
|
||||
|
||||
.. _life-cycle:
|
||||
|
||||
Object Life Cycle
|
||||
=================
|
||||
|
||||
This section explains how a type's slots relate to each other throughout the
|
||||
life of an object. It is not intended to be a complete canonical reference for
|
||||
the slots; instead, refer to the slot-specific documentation in
|
||||
:ref:`type-structs` for details about a particular slot.
|
||||
|
||||
|
||||
Life Events
|
||||
-----------
|
||||
|
||||
The figure below illustrates the order of events that can occur throughout an
|
||||
object's life. An arrow from *A* to *B* indicates that event *B* can occur
|
||||
after event *A* has occurred, with the arrow's label indicating the condition
|
||||
that must be true for *B* to occur after *A*.
|
||||
|
||||
.. only:: html and not epub
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<style type="text/css">
|
||||
|
||||
.. raw:: html
|
||||
:file: lifecycle.dot.css
|
||||
|
||||
.. raw:: html
|
||||
|
||||
</style>
|
||||
|
||||
.. raw:: html
|
||||
:file: lifecycle.dot.svg
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<script>
|
||||
(() => {
|
||||
const g = document.getElementById('life_events_graph');
|
||||
const title = g.querySelector(':scope > title');
|
||||
title.id = 'life-events-graph-title';
|
||||
const svg = g.closest('svg');
|
||||
svg.role = 'img';
|
||||
svg.setAttribute('aria-describedby',
|
||||
'life-events-graph-description');
|
||||
svg.setAttribute('aria-labelledby', 'life-events-graph-title');
|
||||
})();
|
||||
</script>
|
||||
|
||||
.. only:: epub or not (html or latex)
|
||||
|
||||
.. image:: lifecycle.dot.svg
|
||||
:align: center
|
||||
:class: invert-in-dark-mode
|
||||
:alt: Diagram showing events in an object's life. Explained in detail
|
||||
below.
|
||||
|
||||
.. only:: latex
|
||||
|
||||
.. image:: lifecycle.dot.pdf
|
||||
:align: center
|
||||
:class: invert-in-dark-mode
|
||||
:alt: Diagram showing events in an object's life. Explained in detail
|
||||
below.
|
||||
|
||||
.. container::
|
||||
:name: life-events-graph-description
|
||||
|
||||
Explanation:
|
||||
|
||||
* When a new object is constructed by calling its type:
|
||||
|
||||
#. :c:member:`~PyTypeObject.tp_new` is called to create a new object.
|
||||
#. :c:member:`~PyTypeObject.tp_alloc` is directly called by
|
||||
:c:member:`~PyTypeObject.tp_new` to allocate the memory for the new
|
||||
object.
|
||||
#. :c:member:`~PyTypeObject.tp_init` initializes the newly created object.
|
||||
:c:member:`!tp_init` can be called again to re-initialize an object, if
|
||||
desired. The :c:member:`!tp_init` call can also be skipped entirely,
|
||||
for example by Python code calling :py:meth:`~object.__new__`.
|
||||
|
||||
* After :c:member:`!tp_init` completes, the object is ready to use.
|
||||
* Some time after the last reference to an object is removed:
|
||||
|
||||
#. If an object is not marked as *finalized*, it might be finalized by
|
||||
marking it as *finalized* and calling its
|
||||
:c:member:`~PyTypeObject.tp_finalize` function. Python does
|
||||
*not* finalize an object when the last reference to it is deleted; use
|
||||
:c:func:`PyObject_CallFinalizerFromDealloc` to ensure that
|
||||
:c:member:`~PyTypeObject.tp_finalize` is always called.
|
||||
#. If the object is marked as finalized,
|
||||
:c:member:`~PyTypeObject.tp_clear` might be called by the garbage collector
|
||||
to clear references held by the object. It is *not* called when the
|
||||
object's reference count reaches zero.
|
||||
#. :c:member:`~PyTypeObject.tp_dealloc` is called to destroy the object.
|
||||
To avoid code duplication, :c:member:`~PyTypeObject.tp_dealloc` typically
|
||||
calls into :c:member:`~PyTypeObject.tp_clear` to free up the object's
|
||||
references.
|
||||
#. When :c:member:`~PyTypeObject.tp_dealloc` finishes object destruction,
|
||||
it directly calls :c:member:`~PyTypeObject.tp_free` (usually set to
|
||||
:c:func:`PyObject_Free` or :c:func:`PyObject_GC_Del` automatically as
|
||||
appropriate for the type) to deallocate the memory.
|
||||
|
||||
* The :c:member:`~PyTypeObject.tp_finalize` function is permitted to add a
|
||||
reference to the object if desired. If it does, the object is
|
||||
*resurrected*, preventing its pending destruction. (Only
|
||||
:c:member:`!tp_finalize` is allowed to resurrect an object;
|
||||
:c:member:`~PyTypeObject.tp_clear` and
|
||||
:c:member:`~PyTypeObject.tp_dealloc` cannot without calling into
|
||||
:c:member:`!tp_finalize`.) Resurrecting an object may
|
||||
or may not cause the object's *finalized* mark to be removed. Currently,
|
||||
Python does not remove the *finalized* mark from a resurrected object if
|
||||
it supports garbage collection (i.e., the :c:macro:`Py_TPFLAGS_HAVE_GC`
|
||||
flag is set) but does remove the mark if the object does not support
|
||||
garbage collection; either or both of these behaviors may change in the
|
||||
future.
|
||||
* :c:member:`~PyTypeObject.tp_dealloc` can optionally call
|
||||
:c:member:`~PyTypeObject.tp_finalize` via
|
||||
:c:func:`PyObject_CallFinalizerFromDealloc` if it wishes to reuse that
|
||||
code to help with object destruction. This is recommended because it
|
||||
guarantees that :c:member:`!tp_finalize` is always called before
|
||||
destruction. See the :c:member:`~PyTypeObject.tp_dealloc` documentation
|
||||
for example code.
|
||||
* If the object is a member of a :term:`cyclic isolate` and either
|
||||
:c:member:`~PyTypeObject.tp_clear` fails to break the reference cycle or
|
||||
the cyclic isolate is not detected (perhaps :func:`gc.disable` was called,
|
||||
or the :c:macro:`Py_TPFLAGS_HAVE_GC` flag was erroneously omitted in one
|
||||
of the involved types), the objects remain indefinitely uncollectable
|
||||
(they "leak"). See :data:`gc.garbage`.
|
||||
|
||||
If the object is marked as supporting garbage collection (the
|
||||
:c:macro:`Py_TPFLAGS_HAVE_GC` flag is set in
|
||||
:c:member:`~PyTypeObject.tp_flags`), the following events are also possible:
|
||||
|
||||
* The garbage collector occasionally calls
|
||||
:c:member:`~PyTypeObject.tp_traverse` to identify :term:`cyclic isolates
|
||||
<cyclic isolate>`.
|
||||
* When the garbage collector discovers a :term:`cyclic isolate`, it
|
||||
finalizes one of the objects in the group by marking it as *finalized* and
|
||||
calling its :c:member:`~PyTypeObject.tp_finalize` function, if it has one.
|
||||
This repeats until the cyclic isolate doesn't exist or all of the objects
|
||||
have been finalized.
|
||||
* :c:member:`~PyTypeObject.tp_finalize` is permitted to resurrect the object
|
||||
by adding a reference from outside the :term:`cyclic isolate`. The new
|
||||
reference causes the group of objects to no longer form a cyclic isolate
|
||||
(the reference cycle may still exist, but if it does the objects are no
|
||||
longer isolated).
|
||||
* When the garbage collector discovers a :term:`cyclic isolate` and all of
|
||||
the objects in the group have already been marked as *finalized*, the
|
||||
garbage collector clears one or more of the uncleared objects in the group
|
||||
(possibly concurrently) by calling each's
|
||||
:c:member:`~PyTypeObject.tp_clear` function. This repeats as long as the
|
||||
cyclic isolate still exists and not all of the objects have been cleared.
|
||||
|
||||
|
||||
Cyclic Isolate Destruction
|
||||
--------------------------
|
||||
|
||||
Listed below are the stages of life of a hypothetical :term:`cyclic isolate`
|
||||
that continues to exist after each member object is finalized or cleared. It
|
||||
is a memory leak if a cyclic isolate progresses through all of these stages; it should
|
||||
vanish once all objects are cleared, if not sooner. A cyclic isolate can
|
||||
vanish either because the reference cycle is broken or because the objects are
|
||||
no longer isolated due to finalizer resurrection (see
|
||||
:c:member:`~PyTypeObject.tp_finalize`).
|
||||
|
||||
0. **Reachable** (not yet a cyclic isolate): All objects are in their normal,
|
||||
reachable state. A reference cycle could exist, but an external reference
|
||||
means the objects are not yet isolated.
|
||||
#. **Unreachable but consistent:** The final reference from outside the cyclic
|
||||
group of objects has been removed, causing the objects to become isolated
|
||||
(thus a cyclic isolate is born). None of the group's objects have been
|
||||
finalized or cleared yet. The cyclic isolate remains at this stage until
|
||||
some future run of the garbage collector (not necessarily the next run
|
||||
because the next run might not scan every object).
|
||||
#. **Mix of finalized and not finalized:** Objects in a cyclic isolate are
|
||||
finalized one at a time, which means that there is a period of time when the
|
||||
cyclic isolate is composed of a mix of finalized and non-finalized objects.
|
||||
Finalization order is unspecified, so it can appear random. A finalized
|
||||
object must behave in a sane manner when non-finalized objects interact with
|
||||
it, and a non-finalized object must be able to tolerate the finalization of
|
||||
an arbitrary subset of its referents.
|
||||
#. **All finalized:** All objects in a cyclic isolate are finalized before any
|
||||
of them are cleared.
|
||||
#. **Mix of finalized and cleared:** The objects can be cleared serially or
|
||||
concurrently (but with the :term:`GIL` held); either way, some will finish
|
||||
before others. A finalized object must be able to tolerate the clearing of
|
||||
a subset of its referents. :pep:`442` calls this stage "cyclic trash".
|
||||
#. **Leaked:** If a cyclic isolate still exists after all objects in the group
|
||||
have been finalized and cleared, then the objects remain indefinitely
|
||||
uncollectable (see :data:`gc.garbage`). It is a bug if a cyclic isolate
|
||||
reaches this stage---it means the :c:member:`~PyTypeObject.tp_clear` methods
|
||||
of the participating objects have failed to break the reference cycle as
|
||||
required.
|
||||
|
||||
If :c:member:`~PyTypeObject.tp_clear` did not exist, then Python would have no
|
||||
way to safely break a reference cycle. Simply destroying an object in a cyclic
|
||||
isolate would result in a dangling pointer, triggering undefined behavior when
|
||||
an object referencing the destroyed object is itself destroyed. The clearing
|
||||
step makes object destruction a two-phase process: first
|
||||
:c:member:`~PyTypeObject.tp_clear` is called to partially destroy the objects
|
||||
enough to detangle them from each other, then
|
||||
:c:member:`~PyTypeObject.tp_dealloc` is called to complete the destruction.
|
||||
|
||||
Unlike clearing, finalization is not a phase of destruction. A finalized
|
||||
object must still behave properly by continuing to fulfill its design
|
||||
contracts. An object's finalizer is allowed to execute arbitrary Python code,
|
||||
and is even allowed to prevent the impending destruction by adding a reference.
|
||||
The finalizer is only related to destruction by call order---if it runs, it runs
|
||||
before destruction, which starts with :c:member:`~PyTypeObject.tp_clear` (if
|
||||
called) and concludes with :c:member:`~PyTypeObject.tp_dealloc`.
|
||||
|
||||
The finalization step is not necessary to safely reclaim the objects in a
|
||||
cyclic isolate, but its existence makes it easier to design types that behave
|
||||
in a sane manner when objects are cleared. Clearing an object might
|
||||
necessarily leave it in a broken, partially destroyed state---it might be
|
||||
unsafe to call any of the cleared object's methods or access any of its
|
||||
attributes. With finalization, only finalized objects can possibly interact
|
||||
with cleared objects; non-finalized objects are guaranteed to interact with
|
||||
only non-cleared (but potentially finalized) objects.
|
||||
|
||||
To summarize the possible interactions:
|
||||
|
||||
* A non-finalized object might have references to or from non-finalized and
|
||||
finalized objects, but not to or from cleared objects.
|
||||
* A finalized object might have references to or from non-finalized, finalized,
|
||||
and cleared objects.
|
||||
* A cleared object might have references to or from finalized and cleared
|
||||
objects, but not to or from non-finalized objects.
|
||||
|
||||
Without any reference cycles, an object can be simply destroyed once its last
|
||||
reference is deleted; the finalization and clearing steps are not necessary to
|
||||
safely reclaim unused objects. However, it can be useful to automatically call
|
||||
:c:member:`~PyTypeObject.tp_finalize` and :c:member:`~PyTypeObject.tp_clear`
|
||||
before destruction anyway because type design is simplified when all objects
|
||||
always experience the same series of events regardless of whether they
|
||||
participated in a cyclic isolate. Python currently only calls
|
||||
:c:member:`~PyTypeObject.tp_finalize` and :c:member:`~PyTypeObject.tp_clear` as
|
||||
needed to destroy a cyclic isolate; this may change in a future version.
|
||||
|
||||
|
||||
Functions
|
||||
---------
|
||||
|
||||
To allocate and free memory, see :ref:`allocating-objects`.
|
||||
|
||||
|
||||
.. c:function:: void PyObject_CallFinalizer(PyObject *op)
|
||||
|
||||
Finalizes the object as described in :c:member:`~PyTypeObject.tp_finalize`.
|
||||
Call this function (or :c:func:`PyObject_CallFinalizerFromDealloc`) instead
|
||||
of calling :c:member:`~PyTypeObject.tp_finalize` directly because this
|
||||
function may deduplicate multiple calls to :c:member:`!tp_finalize`.
|
||||
Currently, calls are only deduplicated if the type supports garbage
|
||||
collection (i.e., the :c:macro:`Py_TPFLAGS_HAVE_GC` flag is set); this may
|
||||
change in the future.
|
||||
|
||||
|
||||
.. c:function:: int PyObject_CallFinalizerFromDealloc(PyObject *op)
|
||||
|
||||
Same as :c:func:`PyObject_CallFinalizer` but meant to be called at the
|
||||
beginning of the object's destructor (:c:member:`~PyTypeObject.tp_dealloc`).
|
||||
There must not be any references to the object. If the object's finalizer
|
||||
resurrects the object, this function returns -1; no further destruction
|
||||
should happen. Otherwise, this function returns 0 and destruction can
|
||||
continue normally.
|
||||
|
||||
.. seealso::
|
||||
|
||||
:c:member:`~PyTypeObject.tp_dealloc` for example code.
|
||||
|
|
@ -653,3 +653,177 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate.
|
|||
|
||||
.. versionadded:: 3.12
|
||||
|
||||
|
||||
Export API
|
||||
^^^^^^^^^^
|
||||
|
||||
.. versionadded:: 3.14
|
||||
|
||||
.. c:struct:: PyLongLayout
|
||||
|
||||
Layout of an array of "digits" ("limbs" in the GMP terminology), used to
|
||||
represent absolute value for arbitrary precision integers.
|
||||
|
||||
Use :c:func:`PyLong_GetNativeLayout` to get the native layout of Python
|
||||
:class:`int` objects, used internally for integers with "big enough"
|
||||
absolute value.
|
||||
|
||||
See also :data:`sys.int_info` which exposes similar information in Python.
|
||||
|
||||
.. c:member:: uint8_t bits_per_digit
|
||||
|
||||
Bits per digit. For example, a 15 bit digit means that bits 0-14 contain
|
||||
meaningful information.
|
||||
|
||||
.. c:member:: uint8_t digit_size
|
||||
|
||||
Digit size in bytes. For example, a 15 bit digit will require at least 2
|
||||
bytes.
|
||||
|
||||
.. c:member:: int8_t digits_order
|
||||
|
||||
Digits order:
|
||||
|
||||
- ``1`` for most significant digit first
|
||||
- ``-1`` for least significant digit first
|
||||
|
||||
.. c:member:: int8_t digit_endianness
|
||||
|
||||
Digit endianness:
|
||||
|
||||
- ``1`` for most significant byte first (big endian)
|
||||
- ``-1`` for least significant byte first (little endian)
|
||||
|
||||
|
||||
.. c:function:: const PyLongLayout* PyLong_GetNativeLayout(void)
|
||||
|
||||
Get the native layout of Python :class:`int` objects.
|
||||
|
||||
See the :c:struct:`PyLongLayout` structure.
|
||||
|
||||
The function must not be called before Python initialization nor after
|
||||
Python finalization. The returned layout is valid until Python is
|
||||
finalized. The layout is the same for all Python sub-interpreters
|
||||
in a process, and so it can be cached.
|
||||
|
||||
|
||||
.. c:struct:: PyLongExport
|
||||
|
||||
Export of a Python :class:`int` object.
|
||||
|
||||
There are two cases:
|
||||
|
||||
* If :c:member:`digits` is ``NULL``, only use the :c:member:`value` member.
|
||||
* If :c:member:`digits` is not ``NULL``, use :c:member:`negative`,
|
||||
:c:member:`ndigits` and :c:member:`digits` members.
|
||||
|
||||
.. c:member:: int64_t value
|
||||
|
||||
The native integer value of the exported :class:`int` object.
|
||||
Only valid if :c:member:`digits` is ``NULL``.
|
||||
|
||||
.. c:member:: uint8_t negative
|
||||
|
||||
``1`` if the number is negative, ``0`` otherwise.
|
||||
Only valid if :c:member:`digits` is not ``NULL``.
|
||||
|
||||
.. c:member:: Py_ssize_t ndigits
|
||||
|
||||
Number of digits in :c:member:`digits` array.
|
||||
Only valid if :c:member:`digits` is not ``NULL``.
|
||||
|
||||
.. c:member:: const void *digits
|
||||
|
||||
Read-only array of unsigned digits. Can be ``NULL``.
|
||||
|
||||
|
||||
.. c:function:: int PyLong_Export(PyObject *obj, PyLongExport *export_long)
|
||||
|
||||
Export a Python :class:`int` object.
|
||||
|
||||
*export_long* must point to a :c:struct:`PyLongExport` structure allocated
|
||||
by the caller. It must not be ``NULL``.
|
||||
|
||||
On success, fill in *\*export_long* and return ``0``.
|
||||
On error, set an exception and return ``-1``.
|
||||
|
||||
:c:func:`PyLong_FreeExport` must be called when the export is no longer
|
||||
needed.
|
||||
|
||||
.. impl-detail::
|
||||
This function always succeeds if *obj* is a Python :class:`int` object
|
||||
or a subclass.
|
||||
|
||||
|
||||
.. c:function:: void PyLong_FreeExport(PyLongExport *export_long)
|
||||
|
||||
Release the export *export_long* created by :c:func:`PyLong_Export`.
|
||||
|
||||
.. impl-detail::
|
||||
Calling :c:func:`PyLong_FreeExport` is optional if *export_long->digits*
|
||||
is ``NULL``.
|
||||
|
||||
|
||||
PyLongWriter API
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
The :c:type:`PyLongWriter` API can be used to import an integer.
|
||||
|
||||
.. versionadded:: 3.14
|
||||
|
||||
.. c:struct:: PyLongWriter
|
||||
|
||||
A Python :class:`int` writer instance.
|
||||
|
||||
The instance must be destroyed by :c:func:`PyLongWriter_Finish` or
|
||||
:c:func:`PyLongWriter_Discard`.
|
||||
|
||||
|
||||
.. c:function:: PyLongWriter* PyLongWriter_Create(int negative, Py_ssize_t ndigits, void **digits)
|
||||
|
||||
Create a :c:type:`PyLongWriter`.
|
||||
|
||||
On success, allocate *\*digits* and return a writer.
|
||||
On error, set an exception and return ``NULL``.
|
||||
|
||||
*negative* is ``1`` if the number is negative, or ``0`` otherwise.
|
||||
|
||||
*ndigits* is the number of digits in the *digits* array. It must be
|
||||
greater than 0.
|
||||
|
||||
*digits* must not be NULL.
|
||||
|
||||
After a successful call to this function, the caller should fill in the
|
||||
array of digits *digits* and then call :c:func:`PyLongWriter_Finish` to get
|
||||
a Python :class:`int`.
|
||||
The layout of *digits* is described by :c:func:`PyLong_GetNativeLayout`.
|
||||
|
||||
Digits must be in the range [``0``; ``(1 << bits_per_digit) - 1``]
|
||||
(where the :c:struct:`~PyLongLayout.bits_per_digit` is the number of bits
|
||||
per digit).
|
||||
Any unused most significant digits must be set to ``0``.
|
||||
|
||||
Alternately, call :c:func:`PyLongWriter_Discard` to destroy the writer
|
||||
instance without creating an :class:`~int` object.
|
||||
|
||||
|
||||
.. c:function:: PyObject* PyLongWriter_Finish(PyLongWriter *writer)
|
||||
|
||||
Finish a :c:type:`PyLongWriter` created by :c:func:`PyLongWriter_Create`.
|
||||
|
||||
On success, return a Python :class:`int` object.
|
||||
On error, set an exception and return ``NULL``.
|
||||
|
||||
The function takes care of normalizing the digits and converts the object
|
||||
to a compact integer if needed.
|
||||
|
||||
The writer instance and the *digits* array are invalid after the call.
|
||||
|
||||
|
||||
.. c:function:: void PyLongWriter_Discard(PyLongWriter *writer)
|
||||
|
||||
Discard a :c:type:`PyLongWriter` created by :c:func:`PyLongWriter_Create`.
|
||||
|
||||
If *writer* is ``NULL``, no operation is performed.
|
||||
|
||||
The writer instance and the *digits* array are invalid after the call.
|
||||
|
|
|
|||
|
|
@ -110,12 +110,12 @@ The three allocation domains are:
|
|||
|
||||
* Raw domain: intended for allocating memory for general-purpose memory
|
||||
buffers where the allocation *must* go to the system allocator or where the
|
||||
allocator can operate without the :term:`GIL`. The memory is requested directly
|
||||
from the system. See :ref:`Raw Memory Interface <raw-memoryinterface>`.
|
||||
allocator can operate without an :term:`attached thread state`. The memory
|
||||
is requested directly from the system. See :ref:`Raw Memory Interface <raw-memoryinterface>`.
|
||||
|
||||
* "Mem" domain: intended for allocating memory for Python buffers and
|
||||
general-purpose memory buffers where the allocation must be performed with
|
||||
the :term:`GIL` held. The memory is taken from the Python private heap.
|
||||
an :term:`attached thread state`. The memory is taken from the Python private heap.
|
||||
See :ref:`Memory Interface <memoryinterface>`.
|
||||
|
||||
* Object domain: intended for allocating memory for Python objects. The
|
||||
|
|
@ -139,8 +139,8 @@ Raw Memory Interface
|
|||
====================
|
||||
|
||||
The following function sets are wrappers to the system allocator. These
|
||||
functions are thread-safe, the :term:`GIL <global interpreter lock>` does not
|
||||
need to be held.
|
||||
functions are thread-safe, so a :term:`thread state` does not
|
||||
need to be :term:`attached <attached thread state>`.
|
||||
|
||||
The :ref:`default raw memory allocator <default-memory-allocators>` uses
|
||||
the following functions: :c:func:`malloc`, :c:func:`calloc`, :c:func:`realloc`
|
||||
|
|
@ -213,8 +213,7 @@ The :ref:`default memory allocator <default-memory-allocators>` uses the
|
|||
|
||||
.. warning::
|
||||
|
||||
The :term:`GIL <global interpreter lock>` must be held when using these
|
||||
functions.
|
||||
There must be an :term:`attached thread state` when using these functions.
|
||||
|
||||
.. versionchanged:: 3.6
|
||||
|
||||
|
|
@ -327,8 +326,7 @@ The :ref:`default object allocator <default-memory-allocators>` uses the
|
|||
|
||||
.. warning::
|
||||
|
||||
The :term:`GIL <global interpreter lock>` must be held when using these
|
||||
functions.
|
||||
There must be an :term:`attached thread state` when using these functions.
|
||||
|
||||
.. c:function:: void* PyObject_Malloc(size_t n)
|
||||
|
||||
|
|
@ -378,6 +376,24 @@ The :ref:`default object allocator <default-memory-allocators>` uses the
|
|||
|
||||
If *p* is ``NULL``, no operation is performed.
|
||||
|
||||
Do not call this directly to free an object's memory; call the type's
|
||||
:c:member:`~PyTypeObject.tp_free` slot instead.
|
||||
|
||||
Do not use this for memory allocated by :c:macro:`PyObject_GC_New` or
|
||||
:c:macro:`PyObject_GC_NewVar`; use :c:func:`PyObject_GC_Del` instead.
|
||||
|
||||
.. seealso::
|
||||
|
||||
* :c:func:`PyObject_GC_Del` is the equivalent of this function for memory
|
||||
allocated by types that support garbage collection.
|
||||
* :c:func:`PyObject_Malloc`
|
||||
* :c:func:`PyObject_Realloc`
|
||||
* :c:func:`PyObject_Calloc`
|
||||
* :c:macro:`PyObject_New`
|
||||
* :c:macro:`PyObject_NewVar`
|
||||
* :c:func:`PyType_GenericAlloc`
|
||||
* :c:member:`~PyTypeObject.tp_free`
|
||||
|
||||
|
||||
.. _default-memory-allocators:
|
||||
|
||||
|
|
@ -485,12 +501,12 @@ Customize Memory Allocators
|
|||
zero bytes.
|
||||
|
||||
For the :c:macro:`PYMEM_DOMAIN_RAW` domain, the allocator must be
|
||||
thread-safe: the :term:`GIL <global interpreter lock>` is not held when the
|
||||
allocator is called.
|
||||
thread-safe: a :term:`thread state` is not :term:`attached <attached thread state>`
|
||||
when the allocator is called.
|
||||
|
||||
For the remaining domains, the allocator must also be thread-safe:
|
||||
the allocator may be called in different interpreters that do not
|
||||
share a ``GIL``.
|
||||
share a :term:`GIL`.
|
||||
|
||||
If the new allocator is not a hook (does not call the previous allocator),
|
||||
the :c:func:`PyMem_SetupDebugHooks` function must be called to reinstall the
|
||||
|
|
@ -507,8 +523,8 @@ Customize Memory Allocators
|
|||
:c:func:`Py_InitializeFromConfig` to install a custom memory
|
||||
allocator. There are no restrictions over the installed allocator
|
||||
other than the ones imposed by the domain (for instance, the Raw
|
||||
Domain allows the allocator to be called without the GIL held). See
|
||||
:ref:`the section on allocator domains <allocator-domains>` for more
|
||||
Domain allows the allocator to be called without an :term:`attached thread state`).
|
||||
See :ref:`the section on allocator domains <allocator-domains>` for more
|
||||
information.
|
||||
|
||||
* If called after Python has finish initializing (after
|
||||
|
|
@ -555,7 +571,7 @@ Runtime checks:
|
|||
called on a memory block allocated by :c:func:`PyMem_Malloc`.
|
||||
- Detect write before the start of the buffer (buffer underflow).
|
||||
- Detect write after the end of the buffer (buffer overflow).
|
||||
- Check that the :term:`GIL <global interpreter lock>` is held when
|
||||
- Check that there is an :term:`attached thread state` when
|
||||
allocator functions of :c:macro:`PYMEM_DOMAIN_OBJ` (ex:
|
||||
:c:func:`PyObject_Malloc`) and :c:macro:`PYMEM_DOMAIN_MEM` (ex:
|
||||
:c:func:`PyMem_Malloc`) domains are called.
|
||||
|
|
@ -620,8 +636,8 @@ PYMEM_CLEANBYTE (meaning uninitialized memory is getting used).
|
|||
The :c:func:`PyMem_SetupDebugHooks` function now also works on Python
|
||||
compiled in release mode. On error, the debug hooks now use
|
||||
:mod:`tracemalloc` to get the traceback where a memory block was allocated.
|
||||
The debug hooks now also check if the GIL is held when functions of
|
||||
:c:macro:`PYMEM_DOMAIN_OBJ` and :c:macro:`PYMEM_DOMAIN_MEM` domains are
|
||||
The debug hooks now also check if there is an :term:`attached thread state` when
|
||||
functions of :c:macro:`PYMEM_DOMAIN_OBJ` and :c:macro:`PYMEM_DOMAIN_MEM` domains are
|
||||
called.
|
||||
|
||||
.. versionchanged:: 3.8
|
||||
|
|
|
|||
|
|
@ -415,7 +415,7 @@ The available slot types are:
|
|||
in one module definition.
|
||||
|
||||
If ``Py_mod_multiple_interpreters`` is not specified, the import
|
||||
machinery defaults to ``Py_MOD_MULTIPLE_INTERPRETERS_NOT_SUPPORTED``.
|
||||
machinery defaults to ``Py_MOD_MULTIPLE_INTERPRETERS_SUPPORTED``.
|
||||
|
||||
.. versionadded:: 3.12
|
||||
|
||||
|
|
@ -523,9 +523,6 @@ state:
|
|||
|
||||
On success, return ``0``. On error, raise an exception and return ``-1``.
|
||||
|
||||
Return ``-1`` if *value* is ``NULL``. It must be called with an exception
|
||||
raised in this case.
|
||||
|
||||
Example usage::
|
||||
|
||||
static int
|
||||
|
|
@ -540,6 +537,10 @@ state:
|
|||
return res;
|
||||
}
|
||||
|
||||
To be convenient, the function accepts ``NULL`` *value* with an exception
|
||||
set. In this case, return ``-1`` and just leave the raised exception
|
||||
unchanged.
|
||||
|
||||
The example can also be written without checking explicitly if *obj* is
|
||||
``NULL``::
|
||||
|
||||
|
|
@ -708,7 +709,7 @@ since multiple such modules can be created from a single definition.
|
|||
mechanisms (either by calling it directly, or by referring to its
|
||||
implementation for details of the required state updates).
|
||||
|
||||
The caller must hold the GIL.
|
||||
The caller must have an :term:`attached thread state`.
|
||||
|
||||
Return ``-1`` with an exception set on error, ``0`` on success.
|
||||
|
||||
|
|
@ -719,6 +720,6 @@ since multiple such modules can be created from a single definition.
|
|||
Removes the module object created from *def* from the interpreter state.
|
||||
Return ``-1`` with an exception set on error, ``0`` on success.
|
||||
|
||||
The caller must hold the GIL.
|
||||
The caller must have an :term:`attached thread state`.
|
||||
|
||||
.. versionadded:: 3.3
|
||||
|
|
|
|||
|
|
@ -75,9 +75,14 @@ See :mod:`sys.monitoring` for descriptions of the events.
|
|||
Fire a ``JUMP`` event.
|
||||
|
||||
|
||||
.. c:function:: int PyMonitoring_FireBranchEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, PyObject *target_offset)
|
||||
.. c:function:: int PyMonitoring_FireBranchLeftEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, PyObject *target_offset)
|
||||
|
||||
Fire a ``BRANCH`` event.
|
||||
Fire a ``BRANCH_LEFT`` event.
|
||||
|
||||
|
||||
.. c:function:: int PyMonitoring_FireBranchRightEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, PyObject *target_offset)
|
||||
|
||||
Fire a ``BRANCH_RIGHT`` event.
|
||||
|
||||
|
||||
.. c:function:: int PyMonitoring_FireCReturnEvent(PyMonitoringState *state, PyObject *codelike, int32_t offset, PyObject *retval)
|
||||
|
|
@ -168,7 +173,8 @@ would typically correspond to a python function.
|
|||
================================================== =====================================
|
||||
Macro Event
|
||||
================================================== =====================================
|
||||
.. c:macro:: PY_MONITORING_EVENT_BRANCH :monitoring-event:`BRANCH`
|
||||
.. c:macro:: PY_MONITORING_EVENT_BRANCH_LEFT :monitoring-event:`BRANCH_LEFT`
|
||||
.. c:macro:: PY_MONITORING_EVENT_BRANCH_RIGHT :monitoring-event:`BRANCH_RIGHT`
|
||||
.. c:macro:: PY_MONITORING_EVENT_CALL :monitoring-event:`CALL`
|
||||
.. c:macro:: PY_MONITORING_EVENT_C_RAISE :monitoring-event:`C_RAISE`
|
||||
.. c:macro:: PY_MONITORING_EVENT_C_RETURN :monitoring-event:`C_RETURN`
|
||||
|
|
@ -190,3 +196,15 @@ would typically correspond to a python function.
|
|||
.. c:function:: int PyMonitoring_ExitScope(void)
|
||||
|
||||
Exit the last scope that was entered with :c:func:`!PyMonitoring_EnterScope`.
|
||||
|
||||
|
||||
.. c:function:: int PY_MONITORING_IS_INSTRUMENTED_EVENT(uint8_t ev)
|
||||
|
||||
Return true if the event corresponding to the event ID *ev* is
|
||||
a :ref:`local event <monitoring-event-local>`.
|
||||
|
||||
.. versionadded:: 3.13
|
||||
|
||||
.. deprecated:: 3.14
|
||||
|
||||
This function is :term:`soft deprecated`.
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ Object Protocol
|
|||
instead of the :func:`repr`.
|
||||
|
||||
|
||||
.. c:function:: int PyObject_HasAttrWithError(PyObject *o, const char *attr_name)
|
||||
.. c:function:: int PyObject_HasAttrWithError(PyObject *o, PyObject *attr_name)
|
||||
|
||||
Returns ``1`` if *o* has the attribute *attr_name*, and ``0`` otherwise.
|
||||
This is equivalent to the Python expression ``hasattr(o, attr_name)``.
|
||||
|
|
@ -111,7 +111,8 @@ Object Protocol
|
|||
.. note::
|
||||
|
||||
Exceptions that occur when this calls :meth:`~object.__getattr__` and
|
||||
:meth:`~object.__getattribute__` methods are silently ignored.
|
||||
:meth:`~object.__getattribute__` methods aren't propagated,
|
||||
but instead given to :func:`sys.unraisablehook`.
|
||||
For proper error handling, use :c:func:`PyObject_HasAttrWithError`,
|
||||
:c:func:`PyObject_GetOptionalAttr` or :c:func:`PyObject_GetAttr` instead.
|
||||
|
||||
|
|
@ -492,6 +493,13 @@ Object Protocol
|
|||
on failure. This is equivalent to the Python statement ``del o[key]``.
|
||||
|
||||
|
||||
.. c:function:: int PyObject_DelItemString(PyObject *o, const char *key)
|
||||
|
||||
This is the same as :c:func:`PyObject_DelItem`, but *key* is
|
||||
specified as a :c:expr:`const char*` UTF-8 encoded bytes string,
|
||||
rather than a :c:expr:`PyObject*`.
|
||||
|
||||
|
||||
.. c:function:: PyObject* PyObject_Dir(PyObject *o)
|
||||
|
||||
This is equivalent to the Python expression ``dir(o)``, returning a (possibly
|
||||
|
|
@ -509,6 +517,12 @@ Object Protocol
|
|||
iterated.
|
||||
|
||||
|
||||
.. c:function:: PyObject* PyObject_SelfIter(PyObject *obj)
|
||||
|
||||
This is equivalent to the Python ``__iter__(self): return self`` method.
|
||||
It is intended for :term:`iterator` types, to be used in the :c:member:`PyTypeObject.tp_iter` slot.
|
||||
|
||||
|
||||
.. c:function:: PyObject* PyObject_GetAIter(PyObject *o)
|
||||
|
||||
This is the equivalent to the Python expression ``aiter(o)``. Takes an
|
||||
|
|
@ -599,3 +613,145 @@ Object Protocol
|
|||
|
||||
.. versionadded:: 3.14
|
||||
|
||||
.. c:function:: int PyUnstable_Object_IsUniqueReferencedTemporary(PyObject *obj)
|
||||
|
||||
Check if *obj* is a unique temporary object.
|
||||
Returns ``1`` if *obj* is known to be a unique temporary object,
|
||||
and ``0`` otherwise. This function cannot fail, but the check is
|
||||
conservative, and may return ``0`` in some cases even if *obj* is a unique
|
||||
temporary object.
|
||||
|
||||
If an object is a unique temporary, it is guaranteed that the current code
|
||||
has the only reference to the object. For arguments to C functions, this
|
||||
should be used instead of checking if the reference count is ``1``. Starting
|
||||
with Python 3.14, the interpreter internally avoids some reference count
|
||||
modifications when loading objects onto the operands stack by
|
||||
:term:`borrowing <borrowed reference>` references when possible, which means
|
||||
that a reference count of ``1`` by itself does not guarantee that a function
|
||||
argument uniquely referenced.
|
||||
|
||||
In the example below, ``my_func`` is called with a unique temporary object
|
||||
as its argument::
|
||||
|
||||
my_func([1, 2, 3])
|
||||
|
||||
In the example below, ``my_func`` is **not** called with a unique temporary
|
||||
object as its argument, even if its refcount is ``1``::
|
||||
|
||||
my_list = [1, 2, 3]
|
||||
my_func(my_list)
|
||||
|
||||
See also the function :c:func:`Py_REFCNT`.
|
||||
|
||||
.. versionadded:: 3.14
|
||||
|
||||
.. c:function:: int PyUnstable_IsImmortal(PyObject *obj)
|
||||
|
||||
This function returns non-zero if *obj* is :term:`immortal`, and zero
|
||||
otherwise. This function cannot fail.
|
||||
|
||||
.. note::
|
||||
|
||||
Objects that are immortal in one CPython version are not guaranteed to
|
||||
be immortal in another.
|
||||
|
||||
.. versionadded:: 3.14
|
||||
|
||||
.. c:function:: int PyUnstable_TryIncRef(PyObject *obj)
|
||||
|
||||
Increments the reference count of *obj* if it is not zero. Returns ``1``
|
||||
if the object's reference count was successfully incremented. Otherwise,
|
||||
this function returns ``0``.
|
||||
|
||||
:c:func:`PyUnstable_EnableTryIncRef` must have been called
|
||||
earlier on *obj* or this function may spuriously return ``0`` in the
|
||||
:term:`free threading` build.
|
||||
|
||||
This function is logically equivalent to the following C code, except that
|
||||
it behaves atomically in the :term:`free threading` build::
|
||||
|
||||
if (Py_REFCNT(op) > 0) {
|
||||
Py_INCREF(op);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
|
||||
This is intended as a building block for managing weak references
|
||||
without the overhead of a Python :ref:`weak reference object <weakrefobjects>`.
|
||||
|
||||
Typically, correct use of this function requires support from *obj*'s
|
||||
deallocator (:c:member:`~PyTypeObject.tp_dealloc`).
|
||||
For example, the following sketch could be adapted to implement a
|
||||
"weakmap" that works like a :py:class:`~weakref.WeakValueDictionary`
|
||||
for a specific type:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
PyMutex mutex;
|
||||
|
||||
PyObject *
|
||||
add_entry(weakmap_key_type *key, PyObject *value)
|
||||
{
|
||||
PyUnstable_EnableTryIncRef(value);
|
||||
weakmap_type weakmap = ...;
|
||||
PyMutex_Lock(&mutex);
|
||||
weakmap_add_entry(weakmap, key, value);
|
||||
PyMutex_Unlock(&mutex);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
PyObject *
|
||||
get_value(weakmap_key_type *key)
|
||||
{
|
||||
weakmap_type weakmap = ...;
|
||||
PyMutex_Lock(&mutex);
|
||||
PyObject *result = weakmap_find(weakmap, key);
|
||||
if (PyUnstable_TryIncRef(result)) {
|
||||
// `result` is safe to use
|
||||
PyMutex_Unlock(&mutex);
|
||||
return result;
|
||||
}
|
||||
// if we get here, `result` is starting to be garbage-collected,
|
||||
// but has not been removed from the weakmap yet
|
||||
PyMutex_Unlock(&mutex);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// tp_dealloc function for weakmap values
|
||||
void
|
||||
value_dealloc(PyObject *value)
|
||||
{
|
||||
weakmap_type weakmap = ...;
|
||||
PyMutex_Lock(&mutex);
|
||||
weakmap_remove_value(weakmap, value);
|
||||
|
||||
...
|
||||
PyMutex_Unlock(&mutex);
|
||||
}
|
||||
|
||||
.. versionadded:: 3.14
|
||||
|
||||
.. c:function:: void PyUnstable_EnableTryIncRef(PyObject *obj)
|
||||
|
||||
Enables subsequent uses of :c:func:`PyUnstable_TryIncRef` on *obj*. The
|
||||
caller must hold a :term:`strong reference` to *obj* when calling this.
|
||||
|
||||
.. versionadded:: 3.14
|
||||
|
||||
.. c:function:: int PyUnstable_Object_IsUniquelyReferenced(PyObject *op)
|
||||
|
||||
Determine if *op* only has one reference.
|
||||
|
||||
On GIL-enabled builds, this function is equivalent to
|
||||
:c:expr:`Py_REFCNT(op) == 1`.
|
||||
|
||||
On a :term:`free threaded <free threading>` build, this checks if *op*'s
|
||||
:term:`reference count` is equal to one and additionally checks if *op*
|
||||
is only used by this thread. :c:expr:`Py_REFCNT(op) == 1` is **not**
|
||||
thread-safe on free threaded builds; prefer this function.
|
||||
|
||||
The caller must hold an :term:`attached thread state`, despite the fact
|
||||
that this function doesn't call into the Python interpreter. This function
|
||||
cannot fail.
|
||||
|
||||
.. versionadded:: 3.14
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ object types.
|
|||
.. toctree::
|
||||
|
||||
allocation.rst
|
||||
lifecycle.rst
|
||||
structures.rst
|
||||
typeobj.rst
|
||||
gcsupport.rst
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ kernel/git/torvalds/linux.git/tree/tools/perf/Documentation/jit-interface.txt>`_
|
|||
In Python, these helper APIs can be used by libraries and features that rely
|
||||
on generating machine code on the fly.
|
||||
|
||||
Note that holding the Global Interpreter Lock (GIL) is not required for these APIs.
|
||||
Note that holding an :term:`attached thread state` is not required for these APIs.
|
||||
|
||||
.. c:function:: int PyUnstable_PerfMapState_Init(void)
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,15 @@ of Python objects.
|
|||
|
||||
Use the :c:func:`Py_SET_REFCNT()` function to set an object reference count.
|
||||
|
||||
.. note::
|
||||
|
||||
On :term:`free threaded <free threading>` builds of Python, returning 1
|
||||
isn't sufficient to determine if it's safe to treat *o* as having no
|
||||
access by other threads. Use :c:func:`PyUnstable_Object_IsUniquelyReferenced`
|
||||
for that instead.
|
||||
|
||||
See also the function :c:func:`PyUnstable_Object_IsUniqueReferencedTemporary()`.
|
||||
|
||||
.. versionchanged:: 3.10
|
||||
:c:func:`Py_REFCNT()` is changed to the inline static function.
|
||||
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ Reflection
|
|||
|
||||
.. c:function:: PyFrameObject* PyEval_GetFrame(void)
|
||||
|
||||
Return the current thread state's frame, which is ``NULL`` if no frame is
|
||||
Return the :term:`attached thread state`'s frame, which is ``NULL`` if no frame is
|
||||
currently executing.
|
||||
|
||||
See also :c:func:`PyThreadState_GetFrame`.
|
||||
|
|
|
|||
|
|
@ -105,6 +105,15 @@ Sequence Protocol
|
|||
equivalent to the Python expression ``value in o``.
|
||||
|
||||
|
||||
.. c:function:: int PySequence_In(PyObject *o, PyObject *value)
|
||||
|
||||
Alias for :c:func:`PySequence_Contains`.
|
||||
|
||||
.. deprecated:: 3.14
|
||||
The function is :term:`soft deprecated` and should no longer be used to
|
||||
write new code.
|
||||
|
||||
|
||||
.. c:function:: Py_ssize_t PySequence_Index(PyObject *o, PyObject *value)
|
||||
|
||||
Return the first index *i* for which ``o[i] == value``. On error, return
|
||||
|
|
|
|||
|
|
@ -118,6 +118,12 @@ Ellipsis Object
|
|||
^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
.. c:var:: PyTypeObject PyEllipsis_Type
|
||||
|
||||
The type of Python :const:`Ellipsis` object. Same as :class:`types.EllipsisType`
|
||||
in the Python layer.
|
||||
|
||||
|
||||
.. c:var:: PyObject *Py_Ellipsis
|
||||
|
||||
The Python ``Ellipsis`` object. This object has no methods. Like
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ Limited C API
|
|||
|
||||
Python 3.2 introduced the *Limited API*, a subset of Python's C API.
|
||||
Extensions that only use the Limited API can be
|
||||
compiled once and work with multiple versions of Python.
|
||||
compiled once and be loaded on multiple versions of Python.
|
||||
Contents of the Limited API are :ref:`listed below <limited-api-list>`.
|
||||
|
||||
.. c:macro:: Py_LIMITED_API
|
||||
|
|
@ -76,7 +76,7 @@ Contents of the Limited API are :ref:`listed below <limited-api-list>`.
|
|||
|
||||
Define ``Py_LIMITED_API`` to the value of :c:macro:`PY_VERSION_HEX`
|
||||
corresponding to the lowest Python version your extension supports.
|
||||
The extension will work without recompilation with all Python 3 releases
|
||||
The extension will be ABI-compatible with all Python 3 releases
|
||||
from the specified one onward, and can use Limited API introduced up to that
|
||||
version.
|
||||
|
||||
|
|
@ -94,7 +94,15 @@ Stable ABI
|
|||
----------
|
||||
|
||||
To enable this, Python provides a *Stable ABI*: a set of symbols that will
|
||||
remain compatible across Python 3.x versions.
|
||||
remain ABI-compatible across Python 3.x versions.
|
||||
|
||||
.. note::
|
||||
|
||||
The Stable ABI prevents ABI issues, like linker errors due to missing
|
||||
symbols or data corruption due to changes in structure layouts or function
|
||||
signatures.
|
||||
However, other changes in Python can change the *behavior* of extensions.
|
||||
See Python's Backwards Compatibility Policy (:pep:`387`) for details.
|
||||
|
||||
The Stable ABI contains symbols exposed in the :ref:`Limited API
|
||||
<limited-c-api>`, but also other ones – for example, functions necessary to
|
||||
|
|
|
|||
|
|
@ -63,6 +63,11 @@ under :ref:`reference counting <countingrefs>`.
|
|||
See documentation of :c:type:`PyVarObject` above.
|
||||
|
||||
|
||||
.. c:var:: PyTypeObject PyBaseObject_Type
|
||||
|
||||
The base class of all other objects, the same as :class:`object` in Python.
|
||||
|
||||
|
||||
.. c:function:: int Py_Is(PyObject *x, PyObject *y)
|
||||
|
||||
Test if the *x* object is the *y* object, the same as ``x is y`` in Python.
|
||||
|
|
|
|||
|
|
@ -216,6 +216,38 @@ Operating System Utilities
|
|||
The function now uses the UTF-8 encoding on Windows if
|
||||
:c:member:`PyPreConfig.legacy_windows_fs_encoding` is zero.
|
||||
|
||||
.. c:function:: FILE* Py_fopen(PyObject *path, const char *mode)
|
||||
|
||||
Similar to :c:func:`!fopen`, but *path* is a Python object and
|
||||
an exception is set on error.
|
||||
|
||||
*path* must be a :class:`str` object, a :class:`bytes` object,
|
||||
or a :term:`path-like object`.
|
||||
|
||||
On success, return the new file pointer.
|
||||
On error, set an exception and return ``NULL``.
|
||||
|
||||
The file must be closed by :c:func:`Py_fclose` rather than calling directly
|
||||
:c:func:`!fclose`.
|
||||
|
||||
The file descriptor is created non-inheritable (:pep:`446`).
|
||||
|
||||
The caller must have an :term:`attached thread state`.
|
||||
|
||||
.. versionadded:: 3.14
|
||||
|
||||
|
||||
.. c:function:: int Py_fclose(FILE *file)
|
||||
|
||||
Close a file that was opened by :c:func:`Py_fopen`.
|
||||
|
||||
On success, return ``0``.
|
||||
On error, return ``EOF`` and ``errno`` is set to indicate the error.
|
||||
In either case, any further access (including another call to
|
||||
:c:func:`Py_fclose`) to the stream results in undefined behavior.
|
||||
|
||||
.. versionadded:: 3.14
|
||||
|
||||
|
||||
.. _systemfunctions:
|
||||
|
||||
|
|
@ -346,8 +378,8 @@ accessible to C code. They all work with the current interpreter thread's
|
|||
silently abort the operation by raising an error subclassed from
|
||||
:class:`Exception` (other errors will not be silenced).
|
||||
|
||||
The hook function is always called with the GIL held by the Python
|
||||
interpreter that raised the event.
|
||||
The hook function is always called with an :term:`attached thread state` by
|
||||
the Python interpreter that raised the event.
|
||||
|
||||
See :pep:`578` for a detailed description of auditing. Functions in the
|
||||
runtime and standard library that raise events are listed in the
|
||||
|
|
@ -426,3 +458,7 @@ Process Control
|
|||
function registered last is called first. Each cleanup function will be called
|
||||
at most once. Since Python's internal finalization will have completed before
|
||||
the cleanup function, no Python APIs should be called by *func*.
|
||||
|
||||
.. seealso::
|
||||
|
||||
:c:func:`PyUnstable_AtExit` for passing a ``void *data`` argument.
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ range.
|
|||
system time.)
|
||||
|
||||
As any other C API (unless otherwise specified), the functions must be called
|
||||
with the :term:`GIL` held.
|
||||
with an :term:`attached thread state`.
|
||||
|
||||
.. c:function:: int PyTime_Monotonic(PyTime_t *result)
|
||||
|
||||
|
|
@ -78,29 +78,29 @@ 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.
|
||||
require the caller to have an :term:`attached thread state`.
|
||||
|
||||
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
|
||||
an exception. To get the cause of the error, :term:`attach <attached thread state>` a :term:`thread state`,
|
||||
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.
|
||||
but don't set an exception on error and don't require an :term:`attached thread state`.
|
||||
|
||||
.. 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.
|
||||
but don't set an exception on error and don't require an :term:`attached thread state`.
|
||||
|
||||
.. 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.
|
||||
but don't set an exception on error and don't require an :term:`attached thread state`.
|
||||
|
||||
|
||||
Conversion functions
|
||||
|
|
|
|||
|
|
@ -82,6 +82,9 @@ Type Objects
|
|||
error (e.g. no more watcher IDs available), return ``-1`` and set an
|
||||
exception.
|
||||
|
||||
In free-threaded builds, :c:func:`PyType_AddWatcher` is not thread-safe,
|
||||
so it must be called at start up (before spawning the first thread).
|
||||
|
||||
.. versionadded:: 3.12
|
||||
|
||||
|
||||
|
|
@ -148,14 +151,29 @@ Type Objects
|
|||
|
||||
.. c:function:: PyObject* PyType_GenericAlloc(PyTypeObject *type, Py_ssize_t nitems)
|
||||
|
||||
Generic handler for the :c:member:`~PyTypeObject.tp_alloc` slot of a type object. Use
|
||||
Python's default memory allocation mechanism to allocate a new instance and
|
||||
initialize all its contents to ``NULL``.
|
||||
Generic handler for the :c:member:`~PyTypeObject.tp_alloc` slot of a type
|
||||
object. Uses Python's default memory allocation mechanism to allocate memory
|
||||
for a new instance, zeros the memory, then initializes the memory as if by
|
||||
calling :c:func:`PyObject_Init` or :c:func:`PyObject_InitVar`.
|
||||
|
||||
Do not call this directly to allocate memory for an object; call the type's
|
||||
:c:member:`~PyTypeObject.tp_alloc` slot instead.
|
||||
|
||||
For types that support garbage collection (i.e., the
|
||||
:c:macro:`Py_TPFLAGS_HAVE_GC` flag is set), this function behaves like
|
||||
:c:macro:`PyObject_GC_New` or :c:macro:`PyObject_GC_NewVar` (except the
|
||||
memory is guaranteed to be zeroed before initialization), and should be
|
||||
paired with :c:func:`PyObject_GC_Del` in :c:member:`~PyTypeObject.tp_free`.
|
||||
Otherwise, it behaves like :c:macro:`PyObject_New` or
|
||||
:c:macro:`PyObject_NewVar` (except the memory is guaranteed to be zeroed
|
||||
before initialization) and should be paired with :c:func:`PyObject_Free` in
|
||||
:c:member:`~PyTypeObject.tp_free`.
|
||||
|
||||
.. c:function:: PyObject* PyType_GenericNew(PyTypeObject *type, PyObject *args, PyObject *kwds)
|
||||
|
||||
Generic handler for the :c:member:`~PyTypeObject.tp_new` slot of a type object. Create a
|
||||
new instance using the type's :c:member:`~PyTypeObject.tp_alloc` slot.
|
||||
Generic handler for the :c:member:`~PyTypeObject.tp_new` slot of a type
|
||||
object. Creates a new instance using the type's
|
||||
:c:member:`~PyTypeObject.tp_alloc` slot and returns the resulting object.
|
||||
|
||||
.. c:function:: int PyType_Ready(PyTypeObject *type)
|
||||
|
||||
|
|
@ -311,10 +329,6 @@ The following functions and structs are used to create
|
|||
|
||||
Metaclasses that override :c:member:`~PyTypeObject.tp_new` are not
|
||||
supported, except if ``tp_new`` is ``NULL``.
|
||||
(For backwards compatibility, other ``PyType_From*`` functions allow
|
||||
such metaclasses. They ignore ``tp_new``, which may result in incomplete
|
||||
initialization. This is deprecated and in Python 3.14+ such metaclasses will
|
||||
not be supported.)
|
||||
|
||||
The *bases* argument can be used to specify base classes; it can either
|
||||
be only one class or a tuple of classes.
|
||||
|
|
@ -456,6 +470,9 @@ The following functions and structs are used to create
|
|||
class need *in addition* to the superclass.
|
||||
Use :c:func:`PyObject_GetTypeData` to get a pointer to subclass-specific
|
||||
memory reserved this way.
|
||||
For negative :c:member:`!basicsize`, Python will insert padding when
|
||||
needed to meet :c:member:`~PyTypeObject.tp_basicsize`'s alignment
|
||||
requirements.
|
||||
|
||||
.. versionchanged:: 3.12
|
||||
|
||||
|
|
@ -529,19 +546,19 @@ The following functions and structs are used to create
|
|||
|
||||
The following “offset” fields cannot be set using :c:type:`PyType_Slot`:
|
||||
|
||||
* :c:member:`~PyTypeObject.tp_weaklistoffset`
|
||||
(use :c:macro:`Py_TPFLAGS_MANAGED_WEAKREF` instead if possible)
|
||||
* :c:member:`~PyTypeObject.tp_dictoffset`
|
||||
(use :c:macro:`Py_TPFLAGS_MANAGED_DICT` instead if possible)
|
||||
* :c:member:`~PyTypeObject.tp_vectorcall_offset`
|
||||
(use ``"__vectorcalloffset__"`` in
|
||||
:ref:`PyMemberDef <pymemberdef-offsets>`)
|
||||
* :c:member:`~PyTypeObject.tp_weaklistoffset`
|
||||
(use :c:macro:`Py_TPFLAGS_MANAGED_WEAKREF` instead if possible)
|
||||
* :c:member:`~PyTypeObject.tp_dictoffset`
|
||||
(use :c:macro:`Py_TPFLAGS_MANAGED_DICT` instead if possible)
|
||||
* :c:member:`~PyTypeObject.tp_vectorcall_offset`
|
||||
(use ``"__vectorcalloffset__"`` in
|
||||
:ref:`PyMemberDef <pymemberdef-offsets>`)
|
||||
|
||||
If it is not possible to switch to a ``MANAGED`` flag (for example,
|
||||
for vectorcall or to support Python older than 3.12), specify the
|
||||
offset in :c:member:`Py_tp_members <PyTypeObject.tp_members>`.
|
||||
See :ref:`PyMemberDef documentation <pymemberdef-offsets>`
|
||||
for details.
|
||||
If it is not possible to switch to a ``MANAGED`` flag (for example,
|
||||
for vectorcall or to support Python older than 3.12), specify the
|
||||
offset in :c:member:`Py_tp_members <PyTypeObject.tp_members>`.
|
||||
See :ref:`PyMemberDef documentation <pymemberdef-offsets>`
|
||||
for details.
|
||||
|
||||
The following internal fields cannot be set at all when creating a heap
|
||||
type:
|
||||
|
|
@ -557,20 +574,18 @@ The following functions and structs are used to create
|
|||
To avoid issues, use the *bases* argument of
|
||||
:c:func:`PyType_FromSpecWithBases` instead.
|
||||
|
||||
.. versionchanged:: 3.9
|
||||
.. versionchanged:: 3.9
|
||||
Slots in :c:type:`PyBufferProcs` may be set in the unlimited API.
|
||||
|
||||
Slots in :c:type:`PyBufferProcs` may be set in the unlimited API.
|
||||
.. versionchanged:: 3.11
|
||||
:c:member:`~PyBufferProcs.bf_getbuffer` and
|
||||
:c:member:`~PyBufferProcs.bf_releasebuffer` are now available
|
||||
under the :ref:`limited API <limited-c-api>`.
|
||||
|
||||
.. versionchanged:: 3.11
|
||||
:c:member:`~PyBufferProcs.bf_getbuffer` and
|
||||
:c:member:`~PyBufferProcs.bf_releasebuffer` are now available
|
||||
under the :ref:`limited API <limited-c-api>`.
|
||||
|
||||
.. versionchanged:: 3.14
|
||||
|
||||
The field :c:member:`~PyTypeObject.tp_vectorcall` can now set
|
||||
using ``Py_tp_vectorcall``. See the field's documentation
|
||||
for details.
|
||||
.. versionchanged:: 3.14
|
||||
The field :c:member:`~PyTypeObject.tp_vectorcall` can now set
|
||||
using ``Py_tp_vectorcall``. See the field's documentation
|
||||
for details.
|
||||
|
||||
.. c:member:: void *pfunc
|
||||
|
||||
|
|
|
|||
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
.. _type-structs:
|
||||
|
||||
Type Objects
|
||||
============
|
||||
Type Object Structures
|
||||
======================
|
||||
|
||||
Perhaps one of the most important structures of the Python object system is the
|
||||
structure that defines a new type: the :c:type:`PyTypeObject` structure. Type
|
||||
|
|
@ -79,7 +79,7 @@ Quick Reference
|
|||
| :c:member:`~PyTypeObject.tp_setattro` | :c:type:`setattrofunc` | __setattr__, | X | X | | G |
|
||||
| | | __delattr__ | | | | |
|
||||
+------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+
|
||||
| :c:member:`~PyTypeObject.tp_as_buffer` | :c:type:`PyBufferProcs` * | | | | | % |
|
||||
| :c:member:`~PyTypeObject.tp_as_buffer` | :c:type:`PyBufferProcs` * | :ref:`sub-slots` | | | | % |
|
||||
+------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+
|
||||
| :c:member:`~PyTypeObject.tp_flags` | unsigned long | | X | X | | ? |
|
||||
+------------------------------------------------+-----------------------------------+-------------------+---+---+---+---+
|
||||
|
|
@ -325,9 +325,10 @@ sub-slots
|
|||
+---------------------------------------------------------+-----------------------------------+---------------+
|
||||
| |
|
||||
+---------------------------------------------------------+-----------------------------------+---------------+
|
||||
| :c:member:`~PyBufferProcs.bf_getbuffer` | :c:func:`getbufferproc` | |
|
||||
| :c:member:`~PyBufferProcs.bf_getbuffer` | :c:func:`getbufferproc` | __buffer__ |
|
||||
+---------------------------------------------------------+-----------------------------------+---------------+
|
||||
| :c:member:`~PyBufferProcs.bf_releasebuffer` | :c:func:`releasebufferproc` | |
|
||||
| :c:member:`~PyBufferProcs.bf_releasebuffer` | :c:func:`releasebufferproc` | __release_\ |
|
||||
| | | buffer\__ |
|
||||
+---------------------------------------------------------+-----------------------------------+---------------+
|
||||
|
||||
.. _slot-typedefs-table:
|
||||
|
|
@ -473,7 +474,7 @@ PyTypeObject Definition
|
|||
-----------------------
|
||||
|
||||
The structure definition for :c:type:`PyTypeObject` can be found in
|
||||
:file:`Include/object.h`. For convenience of reference, this repeats the
|
||||
:file:`Include/cpython/object.h`. For convenience of reference, this repeats the
|
||||
definition found there:
|
||||
|
||||
.. XXX Drop this?
|
||||
|
|
@ -537,6 +538,9 @@ PyVarObject Slots
|
|||
initialized to zero. For :ref:`dynamically allocated type objects
|
||||
<heap-types>`, this field has a special internal meaning.
|
||||
|
||||
This field should be accessed using the :c:func:`Py_SIZE()` and
|
||||
:c:func:`Py_SET_SIZE()` macros.
|
||||
|
||||
**Inheritance:**
|
||||
|
||||
This field is not inherited by subtypes.
|
||||
|
|
@ -587,119 +591,208 @@ and :c:data:`PyType_Type` effectively act as defaults.)
|
|||
|
||||
|
||||
.. c:member:: Py_ssize_t PyTypeObject.tp_basicsize
|
||||
Py_ssize_t PyTypeObject.tp_itemsize
|
||||
Py_ssize_t PyTypeObject.tp_itemsize
|
||||
|
||||
These fields allow calculating the size in bytes of instances of the type.
|
||||
|
||||
There are two kinds of types: types with fixed-length instances have a zero
|
||||
:c:member:`~PyTypeObject.tp_itemsize` field, types with variable-length instances have a non-zero
|
||||
:c:member:`~PyTypeObject.tp_itemsize` field. For a type with fixed-length instances, all
|
||||
instances have the same size, given in :c:member:`~PyTypeObject.tp_basicsize`.
|
||||
:c:member:`!tp_itemsize` field, types with variable-length instances have a non-zero
|
||||
:c:member:`!tp_itemsize` field. For a type with fixed-length instances, all
|
||||
instances have the same size, given in :c:member:`!tp_basicsize`.
|
||||
(Exceptions to this rule can be made using
|
||||
:c:func:`PyUnstable_Object_GC_NewWithExtraData`.)
|
||||
|
||||
For a type with variable-length instances, the instances must have an
|
||||
:c:member:`~PyVarObject.ob_size` field, and the instance size is :c:member:`~PyTypeObject.tp_basicsize` plus N
|
||||
times :c:member:`~PyTypeObject.tp_itemsize`, where N is the "length" of the object. The value of
|
||||
N is typically stored in the instance's :c:member:`~PyVarObject.ob_size` field. There are
|
||||
exceptions: for example, ints use a negative :c:member:`~PyVarObject.ob_size` to indicate a
|
||||
negative number, and N is ``abs(ob_size)`` there. Also, the presence of an
|
||||
:c:member:`~PyVarObject.ob_size` field in the instance layout doesn't mean that the instance
|
||||
structure is variable-length (for example, the structure for the list type has
|
||||
fixed-length instances, yet those instances have a meaningful :c:member:`~PyVarObject.ob_size`
|
||||
field).
|
||||
:c:member:`~PyVarObject.ob_size` field, and the instance size is
|
||||
:c:member:`!tp_basicsize` plus N times :c:member:`!tp_itemsize`,
|
||||
where N is the "length" of the object.
|
||||
|
||||
The basic size includes the fields in the instance declared by the macro
|
||||
:c:macro:`PyObject_HEAD` or :c:macro:`PyObject_VAR_HEAD` (whichever is used to
|
||||
declare the instance struct) and this in turn includes the :c:member:`~PyObject._ob_prev` and
|
||||
:c:member:`~PyObject._ob_next` fields if they are present. This means that the only correct
|
||||
way to get an initializer for the :c:member:`~PyTypeObject.tp_basicsize` is to use the
|
||||
Functions like :c:func:`PyObject_NewVar` will take the value of N as an
|
||||
argument, and store in the instance's :c:member:`~PyVarObject.ob_size` field.
|
||||
Note that the :c:member:`~PyVarObject.ob_size` field may later be used for
|
||||
other purposes. For example, :py:type:`int` instances use the bits of
|
||||
:c:member:`~PyVarObject.ob_size` in an implementation-defined
|
||||
way; the underlying storage and its size should be accessed using
|
||||
:c:func:`PyLong_Export`.
|
||||
|
||||
.. note::
|
||||
|
||||
The :c:member:`~PyVarObject.ob_size` field should be accessed using
|
||||
the :c:func:`Py_SIZE()` and :c:func:`Py_SET_SIZE()` macros.
|
||||
|
||||
Also, the presence of an :c:member:`~PyVarObject.ob_size` field in the
|
||||
instance layout doesn't mean that the instance structure is variable-length.
|
||||
For example, the :py:type:`list` type has fixed-length instances, yet those
|
||||
instances have a :c:member:`~PyVarObject.ob_size` field.
|
||||
(As with :py:type:`int`, avoid reading lists' :c:member:`!ob_size` directly.
|
||||
Call :c:func:`PyList_Size` instead.)
|
||||
|
||||
The :c:member:`!tp_basicsize` includes size needed for data of the type's
|
||||
:c:member:`~PyTypeObject.tp_base`, plus any extra data needed
|
||||
by each instance.
|
||||
|
||||
The correct way to set :c:member:`!tp_basicsize` is to use the
|
||||
``sizeof`` operator on the struct used to declare the instance layout.
|
||||
The basic size does not include the GC header size.
|
||||
This struct must include the struct used to declare the base type.
|
||||
In other words, :c:member:`!tp_basicsize` must be greater than or equal
|
||||
to the base's :c:member:`!tp_basicsize`.
|
||||
|
||||
A note about alignment: if the variable items require a particular alignment,
|
||||
this should be taken care of by the value of :c:member:`~PyTypeObject.tp_basicsize`. Example:
|
||||
suppose a type implements an array of ``double``. :c:member:`~PyTypeObject.tp_itemsize` is
|
||||
``sizeof(double)``. It is the programmer's responsibility that
|
||||
:c:member:`~PyTypeObject.tp_basicsize` is a multiple of ``sizeof(double)`` (assuming this is the
|
||||
alignment requirement for ``double``).
|
||||
Since every type is a subtype of :py:type:`object`, this struct must
|
||||
include :c:type:`PyObject` or :c:type:`PyVarObject` (depending on
|
||||
whether :c:member:`~PyVarObject.ob_size` should be included). These are
|
||||
usually defined by the macro :c:macro:`PyObject_HEAD` or
|
||||
:c:macro:`PyObject_VAR_HEAD`, respectively.
|
||||
|
||||
For any type with variable-length instances, this field must not be ``NULL``.
|
||||
The basic size does not include the GC header size, as that header is not
|
||||
part of :c:macro:`PyObject_HEAD`.
|
||||
|
||||
For cases where struct used to declare the base type is unknown,
|
||||
see :c:member:`PyType_Spec.basicsize` and :c:func:`PyType_FromMetaclass`.
|
||||
|
||||
Notes about alignment:
|
||||
|
||||
- :c:member:`!tp_basicsize` must be a multiple of ``_Alignof(PyObject)``.
|
||||
When using ``sizeof`` on a ``struct`` that includes
|
||||
:c:macro:`PyObject_HEAD`, as recommended, the compiler ensures this.
|
||||
When not using a C ``struct``, or when using compiler
|
||||
extensions like ``__attribute__((packed))``, it is up to you.
|
||||
- If the variable items require a particular alignment,
|
||||
:c:member:`!tp_basicsize` and :c:member:`!tp_itemsize` must each be a
|
||||
multiple of that alignment.
|
||||
For example, if a type's variable part stores a ``double``, it is
|
||||
your responsibility that both fields are a multiple of
|
||||
``_Alignof(double)``.
|
||||
|
||||
**Inheritance:**
|
||||
|
||||
These fields are inherited separately by subtypes. If the base type has a
|
||||
non-zero :c:member:`~PyTypeObject.tp_itemsize`, it is generally not safe to set
|
||||
These fields are inherited separately by subtypes.
|
||||
(That is, if the field is set to zero, :c:func:`PyType_Ready` will copy
|
||||
the value from the base type, indicating that the instances do not
|
||||
need additional storage.)
|
||||
|
||||
If the base type has a non-zero :c:member:`~PyTypeObject.tp_itemsize`, it is generally not safe to set
|
||||
:c:member:`~PyTypeObject.tp_itemsize` to a different non-zero value in a subtype (though this
|
||||
depends on the implementation of the base type).
|
||||
|
||||
|
||||
.. c:member:: destructor PyTypeObject.tp_dealloc
|
||||
|
||||
A pointer to the instance destructor function. This function must be defined
|
||||
unless the type guarantees that its instances will never be deallocated (as is
|
||||
the case for the singletons ``None`` and ``Ellipsis``). The function signature is::
|
||||
A pointer to the instance destructor function. The function signature is::
|
||||
|
||||
void tp_dealloc(PyObject *self);
|
||||
|
||||
The destructor function is called by the :c:func:`Py_DECREF` and
|
||||
:c:func:`Py_XDECREF` macros when the new reference count is zero. At this point,
|
||||
the instance is still in existence, but there are no references to it. The
|
||||
destructor function should free all references which the instance owns, free all
|
||||
memory buffers owned by the instance (using the freeing function corresponding
|
||||
to the allocation function used to allocate the buffer), and call the type's
|
||||
:c:member:`~PyTypeObject.tp_free` function. If the type is not subtypable
|
||||
(doesn't have the :c:macro:`Py_TPFLAGS_BASETYPE` flag bit set), it is
|
||||
permissible to call the object deallocator directly instead of via
|
||||
:c:member:`~PyTypeObject.tp_free`. The object deallocator should be the one used to allocate the
|
||||
instance; this is normally :c:func:`PyObject_Free` if the instance was allocated
|
||||
using :c:macro:`PyObject_New` or :c:macro:`PyObject_NewVar`, or
|
||||
:c:func:`PyObject_GC_Del` if the instance was allocated using
|
||||
:c:macro:`PyObject_GC_New` or :c:macro:`PyObject_GC_NewVar`.
|
||||
The destructor function should remove all references which the instance owns
|
||||
(e.g., call :c:func:`Py_CLEAR`), free all memory buffers owned by the
|
||||
instance, and call the type's :c:member:`~PyTypeObject.tp_free` function to
|
||||
free the object itself.
|
||||
|
||||
If the type supports garbage collection (has the :c:macro:`Py_TPFLAGS_HAVE_GC`
|
||||
flag bit set), the destructor should call :c:func:`PyObject_GC_UnTrack`
|
||||
No guarantees are made about when an object is destroyed, except:
|
||||
|
||||
* Python will destroy an object immediately or some time after the final
|
||||
reference to the object is deleted, unless its finalizer
|
||||
(:c:member:`~PyTypeObject.tp_finalize`) subsequently resurrects the
|
||||
object.
|
||||
* An object will not be destroyed while it is being automatically finalized
|
||||
(:c:member:`~PyTypeObject.tp_finalize`) or automatically cleared
|
||||
(:c:member:`~PyTypeObject.tp_clear`).
|
||||
|
||||
CPython currently destroys an object immediately from :c:func:`Py_DECREF`
|
||||
when the new reference count is zero, but this may change in a future
|
||||
version.
|
||||
|
||||
It is recommended to call :c:func:`PyObject_CallFinalizerFromDealloc` at the
|
||||
beginning of :c:member:`!tp_dealloc` to guarantee that the object is always
|
||||
finalized before destruction.
|
||||
|
||||
If the type supports garbage collection (the :c:macro:`Py_TPFLAGS_HAVE_GC`
|
||||
flag is set), the destructor should call :c:func:`PyObject_GC_UnTrack`
|
||||
before clearing any member fields.
|
||||
|
||||
.. code-block:: c
|
||||
It is permissible to call :c:member:`~PyTypeObject.tp_clear` from
|
||||
:c:member:`!tp_dealloc` to reduce code duplication and to guarantee that the
|
||||
object is always cleared before destruction. Beware that
|
||||
:c:member:`!tp_clear` might have already been called.
|
||||
|
||||
static void foo_dealloc(foo_object *self) {
|
||||
If the type is heap allocated (:c:macro:`Py_TPFLAGS_HEAPTYPE`), the
|
||||
deallocator should release the owned reference to its type object (via
|
||||
:c:func:`Py_DECREF`) after calling the type deallocator. See the example
|
||||
code below.::
|
||||
|
||||
static void
|
||||
foo_dealloc(PyObject *op)
|
||||
{
|
||||
foo_object *self = (foo_object *) op;
|
||||
PyObject_GC_UnTrack(self);
|
||||
Py_CLEAR(self->ref);
|
||||
Py_TYPE(self)->tp_free((PyObject *)self);
|
||||
}
|
||||
Py_TYPE(self)->tp_free(self);
|
||||
}
|
||||
|
||||
Finally, if the type is heap allocated (:c:macro:`Py_TPFLAGS_HEAPTYPE`), the
|
||||
deallocator should release the owned reference to its type object
|
||||
(via :c:func:`Py_DECREF`) after
|
||||
calling the type deallocator. In order to avoid dangling pointers, the
|
||||
recommended way to achieve this is:
|
||||
:c:member:`!tp_dealloc` must leave the exception status unchanged. If it
|
||||
needs to call something that might raise an exception, the exception state
|
||||
must be backed up first and restored later (after logging any exceptions
|
||||
with :c:func:`PyErr_WriteUnraisable`).
|
||||
|
||||
.. code-block:: c
|
||||
Example::
|
||||
|
||||
static void foo_dealloc(foo_object *self) {
|
||||
PyTypeObject *tp = Py_TYPE(self);
|
||||
// free references and buffers here
|
||||
tp->tp_free(self);
|
||||
Py_DECREF(tp);
|
||||
}
|
||||
static void
|
||||
foo_dealloc(PyObject *self)
|
||||
{
|
||||
PyObject *exc = PyErr_GetRaisedException();
|
||||
|
||||
.. warning::
|
||||
if (PyObject_CallFinalizerFromDealloc(self) < 0) {
|
||||
// self was resurrected.
|
||||
goto done;
|
||||
}
|
||||
|
||||
In a garbage collected Python, :c:member:`!tp_dealloc` may be called from
|
||||
any Python thread, not just the thread which created the object (if the
|
||||
object becomes part of a refcount cycle, that cycle might be collected by
|
||||
a garbage collection on any thread). This is not a problem for Python
|
||||
API calls, since the thread on which :c:member:`!tp_dealloc` is called
|
||||
will own the Global Interpreter Lock (GIL). However, if the object being
|
||||
destroyed in turn destroys objects from some other C or C++ library, care
|
||||
should be taken to ensure that destroying those objects on the thread
|
||||
which called :c:member:`!tp_dealloc` will not violate any assumptions of
|
||||
the library.
|
||||
PyTypeObject *tp = Py_TYPE(self);
|
||||
|
||||
if (tp->tp_flags & Py_TPFLAGS_HAVE_GC) {
|
||||
PyObject_GC_UnTrack(self);
|
||||
}
|
||||
|
||||
// Optional, but convenient to avoid code duplication.
|
||||
if (tp->tp_clear && tp->tp_clear(self) < 0) {
|
||||
PyErr_WriteUnraisable(self);
|
||||
}
|
||||
|
||||
// Any additional destruction goes here.
|
||||
|
||||
tp->tp_free(self);
|
||||
self = NULL; // In case PyErr_WriteUnraisable() is called below.
|
||||
|
||||
if (tp->tp_flags & Py_TPFLAGS_HEAPTYPE) {
|
||||
Py_CLEAR(tp);
|
||||
}
|
||||
|
||||
done:
|
||||
// Optional, if something was called that might have raised an
|
||||
// exception.
|
||||
if (PyErr_Occurred()) {
|
||||
PyErr_WriteUnraisable(self);
|
||||
}
|
||||
PyErr_SetRaisedException(exc);
|
||||
}
|
||||
|
||||
:c:member:`!tp_dealloc` may be called from
|
||||
any Python thread, not just the thread which created the object (if the
|
||||
object becomes part of a refcount cycle, that cycle might be collected by
|
||||
a garbage collection on any thread). This is not a problem for Python
|
||||
API calls, since the thread on which :c:member:`!tp_dealloc` is called
|
||||
with an :term:`attached thread state`. However, if the object being
|
||||
destroyed in turn destroys objects from some other C library, care
|
||||
should be taken to ensure that destroying those objects on the thread
|
||||
which called :c:member:`!tp_dealloc` will not violate any assumptions of
|
||||
the library.
|
||||
|
||||
|
||||
**Inheritance:**
|
||||
|
||||
This field is inherited by subtypes.
|
||||
|
||||
.. seealso::
|
||||
|
||||
:ref:`life-cycle` for details about how this slot relates to other slots.
|
||||
|
||||
|
||||
.. c:member:: Py_ssize_t PyTypeObject.tp_vectorcall_offset
|
||||
|
||||
|
|
@ -1023,6 +1116,7 @@ and :c:data:`PyType_Type` effectively act as defaults.)
|
|||
:c:macro:`Py_TPFLAGS_HAVE_GC` flag bit is clear in the subtype and the
|
||||
:c:member:`~PyTypeObject.tp_traverse` and :c:member:`~PyTypeObject.tp_clear` fields in the subtype exist and have
|
||||
``NULL`` values.
|
||||
|
||||
.. XXX are most flag bits *really* inherited individually?
|
||||
|
||||
**Default:**
|
||||
|
|
@ -1089,11 +1183,11 @@ and :c:data:`PyType_Type` effectively act as defaults.)
|
|||
.. c:macro:: Py_TPFLAGS_HAVE_GC
|
||||
|
||||
This bit is set when the object supports garbage collection. If this bit
|
||||
is set, instances must be created using :c:macro:`PyObject_GC_New` and
|
||||
destroyed using :c:func:`PyObject_GC_Del`. More information in section
|
||||
:ref:`supporting-cycle-detection`. This bit also implies that the
|
||||
GC-related fields :c:member:`~PyTypeObject.tp_traverse` and :c:member:`~PyTypeObject.tp_clear` are present in
|
||||
the type object.
|
||||
is set, memory for new instances (see :c:member:`~PyTypeObject.tp_alloc`)
|
||||
must be allocated using :c:macro:`PyObject_GC_New` or
|
||||
:c:func:`PyType_GenericAlloc` and deallocated (see
|
||||
:c:member:`~PyTypeObject.tp_free`) using :c:func:`PyObject_GC_Del`. More
|
||||
information in section :ref:`supporting-cycle-detection`.
|
||||
|
||||
**Inheritance:**
|
||||
|
||||
|
|
@ -1373,8 +1467,9 @@ and :c:data:`PyType_Type` effectively act as defaults.)
|
|||
:mod:`!_thread` extension module::
|
||||
|
||||
static int
|
||||
local_traverse(localobject *self, visitproc visit, void *arg)
|
||||
local_traverse(PyObject *op, visitproc visit, void *arg)
|
||||
{
|
||||
localobject *self = (localobject *) op;
|
||||
Py_VISIT(self->args);
|
||||
Py_VISIT(self->kw);
|
||||
Py_VISIT(self->dict);
|
||||
|
|
@ -1429,6 +1524,11 @@ and :c:data:`PyType_Type` effectively act as defaults.)
|
|||
heap-allocated superclass).
|
||||
If they do not, the type object may not be garbage-collected.
|
||||
|
||||
.. note::
|
||||
|
||||
The :c:member:`~PyTypeObject.tp_traverse` function can be called from any
|
||||
thread.
|
||||
|
||||
.. versionchanged:: 3.9
|
||||
|
||||
Heap-allocated types are expected to visit ``Py_TYPE(self)`` in
|
||||
|
|
@ -1448,28 +1548,110 @@ and :c:data:`PyType_Type` effectively act as defaults.)
|
|||
|
||||
.. c:member:: inquiry PyTypeObject.tp_clear
|
||||
|
||||
An optional pointer to a clear function for the garbage collector. This is only
|
||||
used if the :c:macro:`Py_TPFLAGS_HAVE_GC` flag bit is set. The signature is::
|
||||
An optional pointer to a clear function. The signature is::
|
||||
|
||||
int tp_clear(PyObject *);
|
||||
|
||||
The :c:member:`~PyTypeObject.tp_clear` member function is used to break reference cycles in cyclic
|
||||
garbage detected by the garbage collector. Taken together, all :c:member:`~PyTypeObject.tp_clear`
|
||||
functions in the system must combine to break all reference cycles. This is
|
||||
subtle, and if in any doubt supply a :c:member:`~PyTypeObject.tp_clear` function. For example,
|
||||
the tuple type does not implement a :c:member:`~PyTypeObject.tp_clear` function, because it's
|
||||
possible to prove that no reference cycle can be composed entirely of tuples.
|
||||
Therefore the :c:member:`~PyTypeObject.tp_clear` functions of other types must be sufficient to
|
||||
break any cycle containing a tuple. This isn't immediately obvious, and there's
|
||||
rarely a good reason to avoid implementing :c:member:`~PyTypeObject.tp_clear`.
|
||||
The purpose of this function is to break reference cycles that are causing a
|
||||
:term:`cyclic isolate` so that the objects can be safely destroyed. A
|
||||
cleared object is a partially destroyed object; the object is not obligated
|
||||
to satisfy design invariants held during normal use.
|
||||
|
||||
:c:member:`!tp_clear` does not need to delete references to objects that
|
||||
can't participate in reference cycles, such as Python strings or Python
|
||||
integers. However, it may be convenient to clear all references, and write
|
||||
the type's :c:member:`~PyTypeObject.tp_dealloc` function to invoke
|
||||
:c:member:`!tp_clear` to avoid code duplication. (Beware that
|
||||
:c:member:`!tp_clear` might have already been called. Prefer calling
|
||||
idempotent functions like :c:func:`Py_CLEAR`.)
|
||||
|
||||
Any non-trivial cleanup should be performed in
|
||||
:c:member:`~PyTypeObject.tp_finalize` instead of :c:member:`!tp_clear`.
|
||||
|
||||
.. note::
|
||||
|
||||
If :c:member:`!tp_clear` fails to break a reference cycle then the
|
||||
objects in the :term:`cyclic isolate` may remain indefinitely
|
||||
uncollectable ("leak"). See :data:`gc.garbage`.
|
||||
|
||||
.. note::
|
||||
|
||||
Referents (direct and indirect) might have already been cleared; they are
|
||||
not guaranteed to be in a consistent state.
|
||||
|
||||
.. note::
|
||||
|
||||
The :c:member:`~PyTypeObject.tp_clear` function can be called from any
|
||||
thread.
|
||||
|
||||
.. note::
|
||||
|
||||
An object is not guaranteed to be automatically cleared before its
|
||||
destructor (:c:member:`~PyTypeObject.tp_dealloc`) is called.
|
||||
|
||||
This function differs from the destructor
|
||||
(:c:member:`~PyTypeObject.tp_dealloc`) in the following ways:
|
||||
|
||||
* The purpose of clearing an object is to remove references to other objects
|
||||
that might participate in a reference cycle. The purpose of the
|
||||
destructor, on the other hand, is a superset: it must release *all*
|
||||
resources it owns, including references to objects that cannot participate
|
||||
in a reference cycle (e.g., integers) as well as the object's own memory
|
||||
(by calling :c:member:`~PyTypeObject.tp_free`).
|
||||
* When :c:member:`!tp_clear` is called, other objects might still hold
|
||||
references to the object being cleared. Because of this,
|
||||
:c:member:`!tp_clear` must not deallocate the object's own memory
|
||||
(:c:member:`~PyTypeObject.tp_free`). The destructor, on the other hand,
|
||||
is only called when no (strong) references exist, and as such, must
|
||||
safely destroy the object itself by deallocating it.
|
||||
* :c:member:`!tp_clear` might never be automatically called. An object's
|
||||
destructor, on the other hand, will be automatically called some time
|
||||
after the object becomes unreachable (i.e., either there are no references
|
||||
to the object or the object is a member of a :term:`cyclic isolate`).
|
||||
|
||||
No guarantees are made about when, if, or how often Python automatically
|
||||
clears an object, except:
|
||||
|
||||
* Python will not automatically clear an object if it is reachable, i.e.,
|
||||
there is a reference to it and it is not a member of a :term:`cyclic
|
||||
isolate`.
|
||||
* Python will not automatically clear an object if it has not been
|
||||
automatically finalized (see :c:member:`~PyTypeObject.tp_finalize`). (If
|
||||
the finalizer resurrected the object, the object may or may not be
|
||||
automatically finalized again before it is cleared.)
|
||||
* If an object is a member of a :term:`cyclic isolate`, Python will not
|
||||
automatically clear it if any member of the cyclic isolate has not yet
|
||||
been automatically finalized (:c:member:`~PyTypeObject.tp_finalize`).
|
||||
* Python will not destroy an object until after any automatic calls to its
|
||||
:c:member:`!tp_clear` function have returned. This ensures that the act
|
||||
of breaking a reference cycle does not invalidate the ``self`` pointer
|
||||
while :c:member:`!tp_clear` is still executing.
|
||||
* Python will not automatically call :c:member:`!tp_clear` multiple times
|
||||
concurrently.
|
||||
|
||||
CPython currently only automatically clears objects as needed to break
|
||||
reference cycles in a :term:`cyclic isolate`, but future versions might
|
||||
clear objects regularly before their destruction.
|
||||
|
||||
Taken together, all :c:member:`~PyTypeObject.tp_clear` functions in the
|
||||
system must combine to break all reference cycles. This is subtle, and if
|
||||
in any doubt supply a :c:member:`~PyTypeObject.tp_clear` function. For
|
||||
example, the tuple type does not implement a
|
||||
:c:member:`~PyTypeObject.tp_clear` function, because it's possible to prove
|
||||
that no reference cycle can be composed entirely of tuples. Therefore the
|
||||
:c:member:`~PyTypeObject.tp_clear` functions of other types are responsible
|
||||
for breaking any cycle containing a tuple. This isn't immediately obvious,
|
||||
and there's rarely a good reason to avoid implementing
|
||||
:c:member:`~PyTypeObject.tp_clear`.
|
||||
|
||||
Implementations of :c:member:`~PyTypeObject.tp_clear` should drop the instance's references to
|
||||
those of its members that may be Python objects, and set its pointers to those
|
||||
members to ``NULL``, as in the following example::
|
||||
|
||||
static int
|
||||
local_clear(localobject *self)
|
||||
local_clear(PyObject *op)
|
||||
{
|
||||
localobject *self = (localobject *) op;
|
||||
Py_CLEAR(self->key);
|
||||
Py_CLEAR(self->args);
|
||||
Py_CLEAR(self->kw);
|
||||
|
|
@ -1495,18 +1677,6 @@ and :c:data:`PyType_Type` effectively act as defaults.)
|
|||
|
||||
PyObject_ClearManagedDict((PyObject*)self);
|
||||
|
||||
Note that :c:member:`~PyTypeObject.tp_clear` is not *always* called
|
||||
before an instance is deallocated. For example, when reference counting
|
||||
is enough to determine that an object is no longer used, the cyclic garbage
|
||||
collector is not involved and :c:member:`~PyTypeObject.tp_dealloc` is
|
||||
called directly.
|
||||
|
||||
Because the goal of :c:member:`~PyTypeObject.tp_clear` functions is to break reference cycles,
|
||||
it's not necessary to clear contained objects like Python strings or Python
|
||||
integers, which can't participate in reference cycles. On the other hand, it may
|
||||
be convenient to clear all contained Python objects, and write the type's
|
||||
:c:member:`~PyTypeObject.tp_dealloc` function to invoke :c:member:`~PyTypeObject.tp_clear`.
|
||||
|
||||
More information about Python's garbage collection scheme can be found in
|
||||
section :ref:`supporting-cycle-detection`.
|
||||
|
||||
|
|
@ -1519,6 +1689,10 @@ and :c:data:`PyType_Type` effectively act as defaults.)
|
|||
:c:member:`~PyTypeObject.tp_clear` are all inherited from the base type if they are all zero in
|
||||
the subtype.
|
||||
|
||||
.. seealso::
|
||||
|
||||
:ref:`life-cycle` for details about how this slot relates to other slots.
|
||||
|
||||
|
||||
.. c:member:: richcmpfunc PyTypeObject.tp_richcompare
|
||||
|
||||
|
|
@ -1829,7 +2003,7 @@ and :c:data:`PyType_Type` effectively act as defaults.)
|
|||
dictionary, so it is may be more efficient to call :c:func:`PyObject_GetAttr`
|
||||
when accessing an attribute on the object.
|
||||
|
||||
It is an error to set both the :c:macro:`Py_TPFLAGS_MANAGED_WEAKREF` bit and
|
||||
It is an error to set both the :c:macro:`Py_TPFLAGS_MANAGED_DICT` bit and
|
||||
:c:member:`~PyTypeObject.tp_dictoffset`.
|
||||
|
||||
**Inheritance:**
|
||||
|
|
@ -1895,18 +2069,17 @@ and :c:data:`PyType_Type` effectively act as defaults.)
|
|||
|
||||
**Inheritance:**
|
||||
|
||||
This field is inherited by static subtypes, but not by dynamic
|
||||
subtypes (subtypes created by a class statement).
|
||||
Static subtypes inherit this slot, which will be
|
||||
:c:func:`PyType_GenericAlloc` if inherited from :class:`object`.
|
||||
|
||||
:ref:`Heap subtypes <heap-types>` do not inherit this slot.
|
||||
|
||||
**Default:**
|
||||
|
||||
For dynamic subtypes, this field is always set to
|
||||
:c:func:`PyType_GenericAlloc`, to force a standard heap
|
||||
allocation strategy.
|
||||
For heap subtypes, this field is always set to
|
||||
:c:func:`PyType_GenericAlloc`.
|
||||
|
||||
For static subtypes, :c:data:`PyBaseObject_Type` uses
|
||||
:c:func:`PyType_GenericAlloc`. That is the recommended value
|
||||
for all statically defined types.
|
||||
For static subtypes, this slot is inherited (see above).
|
||||
|
||||
|
||||
.. c:member:: newfunc PyTypeObject.tp_new
|
||||
|
|
@ -1954,20 +2127,27 @@ and :c:data:`PyType_Type` effectively act as defaults.)
|
|||
|
||||
void tp_free(void *self);
|
||||
|
||||
An initializer that is compatible with this signature is :c:func:`PyObject_Free`.
|
||||
This function must free the memory allocated by
|
||||
:c:member:`~PyTypeObject.tp_alloc`.
|
||||
|
||||
**Inheritance:**
|
||||
|
||||
This field is inherited by static subtypes, but not by dynamic
|
||||
subtypes (subtypes created by a class statement)
|
||||
Static subtypes inherit this slot, which will be :c:func:`PyObject_Free` if
|
||||
inherited from :class:`object`. Exception: If the type supports garbage
|
||||
collection (i.e., the :c:macro:`Py_TPFLAGS_HAVE_GC` flag is set in
|
||||
:c:member:`~PyTypeObject.tp_flags`) and it would inherit
|
||||
:c:func:`PyObject_Free`, then this slot is not inherited but instead defaults
|
||||
to :c:func:`PyObject_GC_Del`.
|
||||
|
||||
:ref:`Heap subtypes <heap-types>` do not inherit this slot.
|
||||
|
||||
**Default:**
|
||||
|
||||
In dynamic subtypes, this field is set to a deallocator suitable to
|
||||
match :c:func:`PyType_GenericAlloc` and the value of the
|
||||
:c:macro:`Py_TPFLAGS_HAVE_GC` flag bit.
|
||||
For :ref:`heap subtypes <heap-types>`, this slot defaults to a deallocator suitable to match
|
||||
:c:func:`PyType_GenericAlloc` and the value of the
|
||||
:c:macro:`Py_TPFLAGS_HAVE_GC` flag.
|
||||
|
||||
For static subtypes, :c:data:`PyBaseObject_Type` uses :c:func:`PyObject_Free`.
|
||||
For static subtypes, this slot is inherited (see above).
|
||||
|
||||
|
||||
.. c:member:: inquiry PyTypeObject.tp_is_gc
|
||||
|
|
@ -2094,32 +2274,139 @@ and :c:data:`PyType_Type` effectively act as defaults.)
|
|||
|
||||
.. c:member:: destructor PyTypeObject.tp_finalize
|
||||
|
||||
An optional pointer to an instance finalization function. Its signature is::
|
||||
An optional pointer to an instance finalization function. This is the C
|
||||
implementation of the :meth:`~object.__del__` special method. Its signature
|
||||
is::
|
||||
|
||||
void tp_finalize(PyObject *self);
|
||||
|
||||
If :c:member:`~PyTypeObject.tp_finalize` is set, the interpreter calls it once when
|
||||
finalizing an instance. It is called either from the garbage
|
||||
collector (if the instance is part of an isolated reference cycle) or
|
||||
just before the object is deallocated. Either way, it is guaranteed
|
||||
to be called before attempting to break reference cycles, ensuring
|
||||
that it finds the object in a sane state.
|
||||
The primary purpose of finalization is to perform any non-trivial cleanup
|
||||
that must be performed before the object is destroyed, while the object and
|
||||
any other objects it directly or indirectly references are still in a
|
||||
consistent state. The finalizer is allowed to execute
|
||||
arbitrary Python code.
|
||||
|
||||
:c:member:`~PyTypeObject.tp_finalize` should not mutate the current exception status;
|
||||
therefore, a recommended way to write a non-trivial finalizer is::
|
||||
Before Python automatically finalizes an object, some of the object's direct
|
||||
or indirect referents might have themselves been automatically finalized.
|
||||
However, none of the referents will have been automatically cleared
|
||||
(:c:member:`~PyTypeObject.tp_clear`) yet.
|
||||
|
||||
Other non-finalized objects might still be using a finalized object, so the
|
||||
finalizer must leave the object in a sane state (e.g., invariants are still
|
||||
met).
|
||||
|
||||
.. note::
|
||||
|
||||
After Python automatically finalizes an object, Python might start
|
||||
automatically clearing (:c:member:`~PyTypeObject.tp_clear`) the object
|
||||
and its referents (direct and indirect). Cleared objects are not
|
||||
guaranteed to be in a consistent state; a finalized object must be able
|
||||
to tolerate cleared referents.
|
||||
|
||||
.. note::
|
||||
|
||||
An object is not guaranteed to be automatically finalized before its
|
||||
destructor (:c:member:`~PyTypeObject.tp_dealloc`) is called. It is
|
||||
recommended to call :c:func:`PyObject_CallFinalizerFromDealloc` at the
|
||||
beginning of :c:member:`!tp_dealloc` to guarantee that the object is
|
||||
always finalized before destruction.
|
||||
|
||||
.. note::
|
||||
|
||||
The :c:member:`~PyTypeObject.tp_finalize` function can be called from any
|
||||
thread, although the :term:`GIL` will be held.
|
||||
|
||||
.. note::
|
||||
|
||||
The :c:member:`!tp_finalize` function can be called during shutdown,
|
||||
after some global variables have been deleted. See the documentation of
|
||||
the :meth:`~object.__del__` method for details.
|
||||
|
||||
When Python finalizes an object, it behaves like the following algorithm:
|
||||
|
||||
#. Python might mark the object as *finalized*. Currently, Python always
|
||||
marks objects whose type supports garbage collection (i.e., the
|
||||
:c:macro:`Py_TPFLAGS_HAVE_GC` flag is set in
|
||||
:c:member:`~PyTypeObject.tp_flags`) and never marks other types of
|
||||
objects; this might change in a future version.
|
||||
#. If the object is not marked as *finalized* and its
|
||||
:c:member:`!tp_finalize` finalizer function is non-``NULL``, the
|
||||
finalizer function is called.
|
||||
#. If the finalizer function was called and the finalizer made the object
|
||||
reachable (i.e., there is a reference to the object and it is not a
|
||||
member of a :term:`cyclic isolate`), then the finalizer is said to have
|
||||
*resurrected* the object. It is unspecified whether the finalizer can
|
||||
also resurrect the object by adding a new reference to the object that
|
||||
does not make it reachable, i.e., the object is (still) a member of a
|
||||
cyclic isolate.
|
||||
#. If the finalizer resurrected the object, the object's pending destruction
|
||||
is canceled and the object's *finalized* mark might be removed if
|
||||
present. Currently, Python never removes the *finalized* mark; this
|
||||
might change in a future version.
|
||||
|
||||
*Automatic finalization* refers to any finalization performed by Python
|
||||
except via calls to :c:func:`PyObject_CallFinalizer` or
|
||||
:c:func:`PyObject_CallFinalizerFromDealloc`. No guarantees are made about
|
||||
when, if, or how often an object is automatically finalized, except:
|
||||
|
||||
* Python will not automatically finalize an object if it is reachable, i.e.,
|
||||
there is a reference to it and it is not a member of a :term:`cyclic
|
||||
isolate`.
|
||||
* Python will not automatically finalize an object if finalizing it would
|
||||
not mark the object as *finalized*. Currently, this applies to objects
|
||||
whose type does not support garbage collection, i.e., the
|
||||
:c:macro:`Py_TPFLAGS_HAVE_GC` flag is not set. Such objects can still be
|
||||
manually finalized by calling :c:func:`PyObject_CallFinalizer` or
|
||||
:c:func:`PyObject_CallFinalizerFromDealloc`.
|
||||
* Python will not automatically finalize any two members of a :term:`cyclic
|
||||
isolate` concurrently.
|
||||
* Python will not automatically finalize an object after it has
|
||||
automatically cleared (:c:member:`~PyTypeObject.tp_clear`) the object.
|
||||
* If an object is a member of a :term:`cyclic isolate`, Python will not
|
||||
automatically finalize it after automatically clearing (see
|
||||
:c:member:`~PyTypeObject.tp_clear`) any other member.
|
||||
* Python will automatically finalize every member of a :term:`cyclic
|
||||
isolate` before it automatically clears (see
|
||||
:c:member:`~PyTypeObject.tp_clear`) any of them.
|
||||
* If Python is going to automatically clear an object
|
||||
(:c:member:`~PyTypeObject.tp_clear`), it will automatically finalize the
|
||||
object first.
|
||||
|
||||
Python currently only automatically finalizes objects that are members of a
|
||||
:term:`cyclic isolate`, but future versions might finalize objects regularly
|
||||
before their destruction.
|
||||
|
||||
To manually finalize an object, do not call this function directly; call
|
||||
:c:func:`PyObject_CallFinalizer` or
|
||||
:c:func:`PyObject_CallFinalizerFromDealloc` instead.
|
||||
|
||||
:c:member:`~PyTypeObject.tp_finalize` should leave the current exception
|
||||
status unchanged. The recommended way to write a non-trivial finalizer is
|
||||
to back up the exception at the beginning by calling
|
||||
:c:func:`PyErr_GetRaisedException` and restore the exception at the end by
|
||||
calling :c:func:`PyErr_SetRaisedException`. If an exception is encountered
|
||||
in the middle of the finalizer, log and clear it with
|
||||
:c:func:`PyErr_WriteUnraisable` or :c:func:`PyErr_FormatUnraisable`. For
|
||||
example::
|
||||
|
||||
static void
|
||||
local_finalize(PyObject *self)
|
||||
foo_finalize(PyObject *self)
|
||||
{
|
||||
PyObject *error_type, *error_value, *error_traceback;
|
||||
// Save the current exception, if any.
|
||||
PyObject *exc = PyErr_GetRaisedException();
|
||||
|
||||
/* Save the current exception, if any. */
|
||||
PyErr_Fetch(&error_type, &error_value, &error_traceback);
|
||||
// ...
|
||||
|
||||
/* ... */
|
||||
if (do_something_that_might_raise() != success_indicator) {
|
||||
PyErr_WriteUnraisable(self);
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* Restore the saved exception. */
|
||||
PyErr_Restore(error_type, error_value, error_traceback);
|
||||
done:
|
||||
// Restore the saved exception. This silently discards any exception
|
||||
// raised above, so be sure to call PyErr_WriteUnraisable first if
|
||||
// necessary.
|
||||
PyErr_SetRaisedException(exc);
|
||||
}
|
||||
|
||||
**Inheritance:**
|
||||
|
|
@ -2134,7 +2421,13 @@ and :c:data:`PyType_Type` effectively act as defaults.)
|
|||
:c:macro:`Py_TPFLAGS_HAVE_FINALIZE` flags bit in order for this field to be
|
||||
used. This is no longer required.
|
||||
|
||||
.. seealso:: "Safe object finalization" (:pep:`442`)
|
||||
.. seealso::
|
||||
|
||||
* :pep:`442`: "Safe object finalization"
|
||||
* :ref:`life-cycle` for details about how this slot relates to other
|
||||
slots.
|
||||
* :c:func:`PyObject_CallFinalizer`
|
||||
* :c:func:`PyObject_CallFinalizerFromDealloc`
|
||||
|
||||
|
||||
.. c:member:: vectorcallfunc PyTypeObject.tp_vectorcall
|
||||
|
|
|
|||
|
|
@ -31,6 +31,18 @@ Unicode Type
|
|||
These are the basic Unicode object types used for the Unicode implementation in
|
||||
Python:
|
||||
|
||||
.. c:var:: PyTypeObject PyUnicode_Type
|
||||
|
||||
This instance of :c:type:`PyTypeObject` represents the Python Unicode type.
|
||||
It is exposed to Python code as :py:class:`str`.
|
||||
|
||||
|
||||
.. c:var:: PyTypeObject PyUnicodeIter_Type
|
||||
|
||||
This instance of :c:type:`PyTypeObject` represents the Python Unicode
|
||||
iterator type. It is used to iterate over Unicode string objects.
|
||||
|
||||
|
||||
.. c:type:: Py_UCS4
|
||||
Py_UCS2
|
||||
Py_UCS1
|
||||
|
|
@ -42,19 +54,6 @@ Python:
|
|||
.. versionadded:: 3.3
|
||||
|
||||
|
||||
.. c:type:: Py_UNICODE
|
||||
|
||||
This is a typedef of :c:type:`wchar_t`, which is a 16-bit type or 32-bit type
|
||||
depending on the platform.
|
||||
|
||||
.. versionchanged:: 3.3
|
||||
In previous versions, this was a 16-bit type or a 32-bit type depending on
|
||||
whether you selected a "narrow" or "wide" Unicode version of Python at
|
||||
build time.
|
||||
|
||||
.. deprecated-removed:: 3.13 3.15
|
||||
|
||||
|
||||
.. c:type:: PyASCIIObject
|
||||
PyCompactUnicodeObject
|
||||
PyUnicodeObject
|
||||
|
|
@ -66,12 +65,6 @@ Python:
|
|||
.. versionadded:: 3.3
|
||||
|
||||
|
||||
.. c:var:: PyTypeObject PyUnicode_Type
|
||||
|
||||
This instance of :c:type:`PyTypeObject` represents the Python Unicode type. It
|
||||
is exposed to Python code as ``str``.
|
||||
|
||||
|
||||
The following APIs are C macros and static inlined functions for fast checks and
|
||||
access to internal read-only data of Unicode objects:
|
||||
|
||||
|
|
@ -87,16 +80,6 @@ access to internal read-only data of Unicode objects:
|
|||
subtype. This function always succeeds.
|
||||
|
||||
|
||||
.. c:function:: int PyUnicode_READY(PyObject *unicode)
|
||||
|
||||
Returns ``0``. This API is kept only for backward compatibility.
|
||||
|
||||
.. versionadded:: 3.3
|
||||
|
||||
.. deprecated:: 3.10
|
||||
This API does nothing since Python 3.12.
|
||||
|
||||
|
||||
.. c:function:: Py_ssize_t PyUnicode_GET_LENGTH(PyObject *unicode)
|
||||
|
||||
Return the length of the Unicode string, in code points. *unicode* has to be a
|
||||
|
|
@ -149,12 +132,16 @@ access to internal read-only data of Unicode objects:
|
|||
.. c:function:: void PyUnicode_WRITE(int kind, void *data, \
|
||||
Py_ssize_t index, Py_UCS4 value)
|
||||
|
||||
Write into a canonical representation *data* (as obtained with
|
||||
:c:func:`PyUnicode_DATA`). This function performs no sanity checks, and is
|
||||
intended for usage in loops. The caller should cache the *kind* value and
|
||||
*data* pointer as obtained from other calls. *index* is the index in
|
||||
the string (starts at 0) and *value* is the new code point value which should
|
||||
be written to that location.
|
||||
Write the code point *value* to the given zero-based *index* in a string.
|
||||
|
||||
The *kind* value and *data* pointer must have been obtained from a
|
||||
string using :c:func:`PyUnicode_KIND` and :c:func:`PyUnicode_DATA`
|
||||
respectively. You must hold a reference to that string while calling
|
||||
:c:func:`!PyUnicode_WRITE`. All requirements of
|
||||
:c:func:`PyUnicode_WriteChar` also apply.
|
||||
|
||||
The function performs no checks for any of its requirements,
|
||||
and is intended for usage in loops.
|
||||
|
||||
.. versionadded:: 3.3
|
||||
|
||||
|
|
@ -196,6 +183,14 @@ access to internal read-only data of Unicode objects:
|
|||
is not ready.
|
||||
|
||||
|
||||
.. c:function:: unsigned int PyUnicode_IS_ASCII(PyObject *unicode)
|
||||
|
||||
Return true if the string only contains ASCII characters.
|
||||
Equivalent to :py:meth:`str.isascii`.
|
||||
|
||||
.. versionadded:: 3.2
|
||||
|
||||
|
||||
Unicode Character Properties
|
||||
""""""""""""""""""""""""""""
|
||||
|
||||
|
|
@ -256,13 +251,8 @@ the Python configuration.
|
|||
|
||||
.. c:function:: int Py_UNICODE_ISPRINTABLE(Py_UCS4 ch)
|
||||
|
||||
Return ``1`` or ``0`` depending on whether *ch* is a printable character.
|
||||
Nonprintable characters are those characters defined in the Unicode character
|
||||
database as "Other" or "Separator", excepting the ASCII space (0x20) which is
|
||||
considered printable. (Note that printable characters in this context are
|
||||
those which should not be escaped when :func:`repr` is invoked on a string.
|
||||
It has no bearing on the handling of strings written to :data:`sys.stdout` or
|
||||
:data:`sys.stderr`.)
|
||||
Return ``1`` or ``0`` depending on whether *ch* is a printable character,
|
||||
in the sense of :meth:`str.isprintable`.
|
||||
|
||||
|
||||
These APIs can be used for fast direct character conversions:
|
||||
|
|
@ -335,11 +325,29 @@ APIs:
|
|||
to be placed in the string. As an approximation, it can be rounded up to the
|
||||
nearest value in the sequence 127, 255, 65535, 1114111.
|
||||
|
||||
This is the recommended way to allocate a new Unicode object. Objects
|
||||
created using this function are not resizable.
|
||||
|
||||
On error, set an exception and return ``NULL``.
|
||||
|
||||
After creation, the string can be filled by :c:func:`PyUnicode_WriteChar`,
|
||||
:c:func:`PyUnicode_CopyCharacters`, :c:func:`PyUnicode_Fill`,
|
||||
:c:func:`PyUnicode_WRITE` or similar.
|
||||
Since strings are supposed to be immutable, take care to not “use” the
|
||||
result while it is being modified. In particular, before it's filled
|
||||
with its final contents, a string:
|
||||
|
||||
- must not be hashed,
|
||||
- must not be :c:func:`converted to UTF-8 <PyUnicode_AsUTF8AndSize>`,
|
||||
or another non-"canonical" representation,
|
||||
- must not have its reference count changed,
|
||||
- must not be shared with code that might do one of the above.
|
||||
|
||||
This list is not exhaustive. Avoiding these uses is your responsibility;
|
||||
Python does not always check these requirements.
|
||||
|
||||
To avoid accidentally exposing a partially-written string object, prefer
|
||||
using the :c:type:`PyUnicodeWriter` API, or one of the ``PyUnicode_From*``
|
||||
functions below.
|
||||
|
||||
|
||||
.. versionadded:: 3.3
|
||||
|
||||
|
||||
|
|
@ -594,6 +602,14 @@ APIs:
|
|||
Objects other than Unicode or its subtypes will cause a :exc:`TypeError`.
|
||||
|
||||
|
||||
.. c:function:: PyObject* PyUnicode_FromOrdinal(int ordinal)
|
||||
|
||||
Create a Unicode Object from the given Unicode code point *ordinal*.
|
||||
|
||||
The ordinal must be in ``range(0x110000)``. A :exc:`ValueError` is
|
||||
raised in the case it is not.
|
||||
|
||||
|
||||
.. c:function:: PyObject* PyUnicode_FromEncodedObject(PyObject *obj, \
|
||||
const char *encoding, const char *errors)
|
||||
|
||||
|
|
@ -612,6 +628,43 @@ APIs:
|
|||
decref'ing the returned objects.
|
||||
|
||||
|
||||
.. c:function:: void PyUnicode_Append(PyObject **p_left, PyObject *right)
|
||||
|
||||
Append the string *right* to the end of *p_left*.
|
||||
*p_left* must point to a :term:`strong reference` to a Unicode object;
|
||||
:c:func:`!PyUnicode_Append` releases ("steals") this reference.
|
||||
|
||||
On error, set *\*p_left* to ``NULL`` and set an exception.
|
||||
|
||||
On success, set *\*p_left* to a new strong reference to the result.
|
||||
|
||||
|
||||
.. c:function:: void PyUnicode_AppendAndDel(PyObject **p_left, PyObject *right)
|
||||
|
||||
The function is similar to :c:func:`PyUnicode_Append`, with the only
|
||||
difference being that it decrements the reference count of *right* by one.
|
||||
|
||||
|
||||
.. c:function:: PyObject* PyUnicode_BuildEncodingMap(PyObject* string)
|
||||
|
||||
Return a mapping suitable for decoding a custom single-byte encoding.
|
||||
Given a Unicode string *string* of up to 256 characters representing an encoding
|
||||
table, returns either a compact internal mapping object or a dictionary
|
||||
mapping character ordinals to byte values. Raises a :exc:`TypeError` and
|
||||
return ``NULL`` on invalid input.
|
||||
|
||||
.. versionadded:: 3.2
|
||||
|
||||
|
||||
.. c:function:: const char* PyUnicode_GetDefaultEncoding(void)
|
||||
|
||||
Return the name of the default string encoding, ``"utf-8"``.
|
||||
See :func:`sys.getdefaultencoding`.
|
||||
|
||||
The returned string does not need to be freed, and is valid
|
||||
until interpreter shutdown.
|
||||
|
||||
|
||||
.. c:function:: Py_ssize_t PyUnicode_GetLength(PyObject *unicode)
|
||||
|
||||
Return the length of the Unicode object, in code points.
|
||||
|
|
@ -632,9 +685,27 @@ APIs:
|
|||
possible. Returns ``-1`` and sets an exception on error, otherwise returns
|
||||
the number of copied characters.
|
||||
|
||||
The string must not have been “used” yet.
|
||||
See :c:func:`PyUnicode_New` for details.
|
||||
|
||||
.. versionadded:: 3.3
|
||||
|
||||
|
||||
.. c:function:: int PyUnicode_Resize(PyObject **unicode, Py_ssize_t length);
|
||||
|
||||
Resize a Unicode object *\*unicode* to the new *length* in code points.
|
||||
|
||||
Try to resize the string in place (which is usually faster than allocating
|
||||
a new string and copying characters), or create a new string.
|
||||
|
||||
*\*unicode* is modified to point to the new (resized) object and ``0`` is
|
||||
returned on success. Otherwise, ``-1`` is returned and an exception is set,
|
||||
and *\*unicode* is left untouched.
|
||||
|
||||
The function doesn't check string content, the result may not be a
|
||||
string in canonical representation.
|
||||
|
||||
|
||||
.. c:function:: Py_ssize_t PyUnicode_Fill(PyObject *unicode, Py_ssize_t start, \
|
||||
Py_ssize_t length, Py_UCS4 fill_char)
|
||||
|
||||
|
|
@ -644,6 +715,9 @@ APIs:
|
|||
Fail if *fill_char* is bigger than the string maximum character, or if the
|
||||
string has more than 1 reference.
|
||||
|
||||
The string must not have been “used” yet.
|
||||
See :c:func:`PyUnicode_New` for details.
|
||||
|
||||
Return the number of written character, or return ``-1`` and raise an
|
||||
exception on error.
|
||||
|
||||
|
|
@ -653,15 +727,16 @@ APIs:
|
|||
.. c:function:: int PyUnicode_WriteChar(PyObject *unicode, Py_ssize_t index, \
|
||||
Py_UCS4 character)
|
||||
|
||||
Write a character to a string. The string must have been created through
|
||||
:c:func:`PyUnicode_New`. Since Unicode strings are supposed to be immutable,
|
||||
the string must not be shared, or have been hashed yet.
|
||||
Write a *character* to the string *unicode* at the zero-based *index*.
|
||||
Return ``0`` on success, ``-1`` on error with an exception set.
|
||||
|
||||
This function checks that *unicode* is a Unicode object, that the index is
|
||||
not out of bounds, and that the object can be modified safely (i.e. that it
|
||||
its reference count is one).
|
||||
not out of bounds, and that the object's reference count is one).
|
||||
See :c:func:`PyUnicode_WRITE` for a version that skips these checks,
|
||||
making them your responsibility.
|
||||
|
||||
Return ``0`` on success, ``-1`` on error with an exception set.
|
||||
The string must not have been “used” yet.
|
||||
See :c:func:`PyUnicode_New` for details.
|
||||
|
||||
.. versionadded:: 3.3
|
||||
|
||||
|
|
@ -786,16 +861,25 @@ Functions encoding to and decoding from the :term:`filesystem encoding and
|
|||
error handler` (:pep:`383` and :pep:`529`).
|
||||
|
||||
To encode file names to :class:`bytes` during argument parsing, the ``"O&"``
|
||||
converter should be used, passing :c:func:`PyUnicode_FSConverter` as the
|
||||
converter should be used, passing :c:func:`!PyUnicode_FSConverter` as the
|
||||
conversion function:
|
||||
|
||||
.. c:function:: int PyUnicode_FSConverter(PyObject* obj, void* result)
|
||||
|
||||
ParseTuple converter: encode :class:`str` objects -- obtained directly or
|
||||
:ref:`PyArg_Parse\* converter <arg-parsing>`: encode :class:`str` objects -- obtained directly or
|
||||
through the :class:`os.PathLike` interface -- to :class:`bytes` using
|
||||
:c:func:`PyUnicode_EncodeFSDefault`; :class:`bytes` objects are output as-is.
|
||||
*result* must be a :c:expr:`PyBytesObject*` which must be released when it is
|
||||
no longer used.
|
||||
*result* must be an address of a C variable of type :c:expr:`PyObject*`
|
||||
(or :c:expr:`PyBytesObject*`).
|
||||
On success, set the variable to a new :term:`strong reference` to
|
||||
a :ref:`bytes object <bytesobjects>` which must be released
|
||||
when it is no longer used and return a non-zero value
|
||||
(:c:macro:`Py_CLEANUP_SUPPORTED`).
|
||||
Embedded null bytes are not allowed in the result.
|
||||
On failure, return ``0`` with an exception set.
|
||||
|
||||
If *obj* is ``NULL``, the function releases a strong reference
|
||||
stored in the variable referred by *result* and returns ``1``.
|
||||
|
||||
.. versionadded:: 3.1
|
||||
|
||||
|
|
@ -803,16 +887,26 @@ conversion function:
|
|||
Accepts a :term:`path-like object`.
|
||||
|
||||
To decode file names to :class:`str` during argument parsing, the ``"O&"``
|
||||
converter should be used, passing :c:func:`PyUnicode_FSDecoder` as the
|
||||
converter should be used, passing :c:func:`!PyUnicode_FSDecoder` as the
|
||||
conversion function:
|
||||
|
||||
.. c:function:: int PyUnicode_FSDecoder(PyObject* obj, void* result)
|
||||
|
||||
ParseTuple converter: decode :class:`bytes` objects -- obtained either
|
||||
:ref:`PyArg_Parse\* converter <arg-parsing>`: decode :class:`bytes` objects -- obtained either
|
||||
directly or indirectly through the :class:`os.PathLike` interface -- to
|
||||
:class:`str` using :c:func:`PyUnicode_DecodeFSDefaultAndSize`; :class:`str`
|
||||
objects are output as-is. *result* must be a :c:expr:`PyUnicodeObject*` which
|
||||
must be released when it is no longer used.
|
||||
objects are output as-is.
|
||||
*result* must be an address of a C variable of type :c:expr:`PyObject*`
|
||||
(or :c:expr:`PyUnicodeObject*`).
|
||||
On success, set the variable to a new :term:`strong reference` to
|
||||
a :ref:`Unicode object <unicodeobjects>` which must be released
|
||||
when it is no longer used and return a non-zero value
|
||||
(:c:macro:`Py_CLEANUP_SUPPORTED`).
|
||||
Embedded null characters are not allowed in the result.
|
||||
On failure, return ``0`` with an exception set.
|
||||
|
||||
If *obj* is ``NULL``, release the strong reference
|
||||
to the object referred to by *result* and return ``1``.
|
||||
|
||||
.. versionadded:: 3.2
|
||||
|
||||
|
|
@ -949,6 +1043,17 @@ generic ones are documented for simplicity.
|
|||
Generic Codecs
|
||||
""""""""""""""
|
||||
|
||||
The following macro is provided:
|
||||
|
||||
|
||||
.. c:macro:: Py_UNICODE_REPLACEMENT_CHARACTER
|
||||
|
||||
The Unicode code point ``U+FFFD`` (replacement character).
|
||||
|
||||
This Unicode character is used as the replacement character during
|
||||
decoding if the *errors* argument is set to "replace".
|
||||
|
||||
|
||||
These are the generic codec APIs:
|
||||
|
||||
|
||||
|
|
@ -1035,6 +1140,15 @@ These are the UTF-8 codec APIs:
|
|||
|
||||
As :c:func:`PyUnicode_AsUTF8AndSize`, but does not store the size.
|
||||
|
||||
.. warning::
|
||||
|
||||
This function does not have any special behavior for
|
||||
`null characters <https://en.wikipedia.org/wiki/Null_character>`_ embedded within
|
||||
*unicode*. As a result, strings containing null characters will remain in the returned
|
||||
string, which some C functions might interpret as the end of the string, leading to
|
||||
truncation. If truncation is an issue, it is recommended to use :c:func:`PyUnicode_AsUTF8AndSize`
|
||||
instead.
|
||||
|
||||
.. versionadded:: 3.3
|
||||
|
||||
.. versionchanged:: 3.7
|
||||
|
|
@ -1324,6 +1438,13 @@ the user settings on the machine running the codec.
|
|||
in *consumed*.
|
||||
|
||||
|
||||
.. c:function:: PyObject* PyUnicode_DecodeCodePageStateful(int code_page, const char *str, \
|
||||
Py_ssize_t size, const char *errors, Py_ssize_t *consumed)
|
||||
|
||||
Similar to :c:func:`PyUnicode_DecodeMBCSStateful`, except uses the code page
|
||||
specified by *code_page*.
|
||||
|
||||
|
||||
.. c:function:: PyObject* PyUnicode_AsMBCSString(PyObject *unicode)
|
||||
|
||||
Encode a Unicode object using MBCS and return the result as Python bytes
|
||||
|
|
@ -1368,6 +1489,20 @@ They all return ``NULL`` or ``-1`` if an exception occurs.
|
|||
separator. At most *maxsplit* splits will be done. If negative, no limit is
|
||||
set. Separators are not included in the resulting list.
|
||||
|
||||
On error, return ``NULL`` with an exception set.
|
||||
|
||||
Equivalent to :py:meth:`str.split`.
|
||||
|
||||
|
||||
.. c:function:: PyObject* PyUnicode_RSplit(PyObject *unicode, PyObject *sep, Py_ssize_t maxsplit)
|
||||
|
||||
Similar to :c:func:`PyUnicode_Split`, but splitting will be done beginning
|
||||
at the end of the string.
|
||||
|
||||
On error, return ``NULL`` with an exception set.
|
||||
|
||||
Equivalent to :py:meth:`str.rsplit`.
|
||||
|
||||
|
||||
.. c:function:: PyObject* PyUnicode_Splitlines(PyObject *unicode, int keepends)
|
||||
|
||||
|
|
@ -1376,6 +1511,33 @@ They all return ``NULL`` or ``-1`` if an exception occurs.
|
|||
characters are not included in the resulting strings.
|
||||
|
||||
|
||||
.. c:function:: PyObject* PyUnicode_Partition(PyObject *unicode, PyObject *sep)
|
||||
|
||||
Split a Unicode string at the first occurrence of *sep*, and return
|
||||
a 3-tuple containing the part before the separator, the separator itself,
|
||||
and the part after the separator. If the separator is not found,
|
||||
return a 3-tuple containing the string itself, followed by two empty strings.
|
||||
|
||||
*sep* must not be empty.
|
||||
|
||||
On error, return ``NULL`` with an exception set.
|
||||
|
||||
Equivalent to :py:meth:`str.partition`.
|
||||
|
||||
|
||||
.. c:function:: PyObject* PyUnicode_RPartition(PyObject *unicode, PyObject *sep)
|
||||
|
||||
Similar to :c:func:`PyUnicode_Partition`, but split a Unicode string at the
|
||||
last occurrence of *sep*. If the separator is not found, return a 3-tuple
|
||||
containing two empty strings, followed by the string itself.
|
||||
|
||||
*sep* must not be empty.
|
||||
|
||||
On error, return ``NULL`` with an exception set.
|
||||
|
||||
Equivalent to :py:meth:`str.rpartition`.
|
||||
|
||||
|
||||
.. c:function:: PyObject* PyUnicode_Join(PyObject *separator, PyObject *seq)
|
||||
|
||||
Join a sequence of strings using the given *separator* and return the resulting
|
||||
|
|
@ -1569,6 +1731,20 @@ They all return ``NULL`` or ``-1`` if an exception occurs.
|
|||
Strings interned this way are made :term:`immortal`.
|
||||
|
||||
|
||||
.. c:function:: unsigned int PyUnicode_CHECK_INTERNED(PyObject *str)
|
||||
|
||||
Return a non-zero value if *str* is interned, zero if not.
|
||||
The *str* argument must be a string; this is not checked.
|
||||
This function always succeeds.
|
||||
|
||||
.. impl-detail::
|
||||
|
||||
A non-zero return value may carry additional information
|
||||
about *how* the string is interned.
|
||||
The meaning of such non-zero values, as well as each specific string's
|
||||
intern-related details, may change between CPython versions.
|
||||
|
||||
|
||||
PyUnicodeWriter
|
||||
^^^^^^^^^^^^^^^
|
||||
|
||||
|
|
@ -1588,6 +1764,11 @@ object.
|
|||
|
||||
Create a Unicode writer instance.
|
||||
|
||||
*length* must be greater than or equal to ``0``.
|
||||
|
||||
If *length* is greater than ``0``, preallocate an internal buffer of
|
||||
*length* characters.
|
||||
|
||||
Set an exception and return ``NULL`` on error.
|
||||
|
||||
.. c:function:: PyObject* PyUnicodeWriter_Finish(PyUnicodeWriter *writer)
|
||||
|
|
@ -1596,12 +1777,16 @@ object.
|
|||
|
||||
Set an exception and return ``NULL`` on error.
|
||||
|
||||
The writer instance is invalid after this call.
|
||||
|
||||
.. c:function:: void PyUnicodeWriter_Discard(PyUnicodeWriter *writer)
|
||||
|
||||
Discard the internal Unicode buffer and destroy the writer instance.
|
||||
|
||||
If *writer* is ``NULL``, no operation is performed.
|
||||
|
||||
The writer instance is invalid after this call.
|
||||
|
||||
.. c:function:: int PyUnicodeWriter_WriteChar(PyUnicodeWriter *writer, Py_UCS4 ch)
|
||||
|
||||
Write the single Unicode character *ch* into *writer*.
|
||||
|
|
@ -1680,8 +1865,8 @@ object.
|
|||
*size* is the string length in bytes. If *size* is equal to ``-1``, call
|
||||
``strlen(str)`` to get the string length.
|
||||
|
||||
*errors* is an error handler name, such as ``"replace"``. If *errors* is
|
||||
``NULL``, use the strict error handler.
|
||||
*errors* is an :ref:`error handler <error-handlers>` name, such as
|
||||
``"replace"``. If *errors* is ``NULL``, use the strict error handler.
|
||||
|
||||
If *consumed* is not ``NULL``, set *\*consumed* to the number of decoded
|
||||
bytes on success.
|
||||
|
|
@ -1692,3 +1877,49 @@ object.
|
|||
On error, set an exception, leave the writer unchanged, and return ``-1``.
|
||||
|
||||
See also :c:func:`PyUnicodeWriter_WriteUTF8`.
|
||||
|
||||
Deprecated API
|
||||
^^^^^^^^^^^^^^
|
||||
|
||||
The following API is deprecated.
|
||||
|
||||
.. c:type:: Py_UNICODE
|
||||
|
||||
This is a typedef of :c:type:`wchar_t`, which is a 16-bit type or 32-bit type
|
||||
depending on the platform.
|
||||
Please use :c:type:`wchar_t` directly instead.
|
||||
|
||||
.. versionchanged:: 3.3
|
||||
In previous versions, this was a 16-bit type or a 32-bit type depending on
|
||||
whether you selected a "narrow" or "wide" Unicode version of Python at
|
||||
build time.
|
||||
|
||||
.. deprecated-removed:: 3.13 3.15
|
||||
|
||||
|
||||
.. c:function:: int PyUnicode_READY(PyObject *unicode)
|
||||
|
||||
Do nothing and return ``0``.
|
||||
This API is kept only for backward compatibility, but there are no plans
|
||||
to remove it.
|
||||
|
||||
.. versionadded:: 3.3
|
||||
|
||||
.. deprecated:: 3.10
|
||||
This API does nothing since Python 3.12.
|
||||
Previously, this needed to be called for each string created using
|
||||
the old API (:c:func:`!PyUnicode_FromUnicode` or similar).
|
||||
|
||||
|
||||
.. c:function:: unsigned int PyUnicode_IS_READY(PyObject *unicode)
|
||||
|
||||
Do nothing and return ``1``.
|
||||
This API is kept only for backward compatibility, but there are no plans
|
||||
to remove it.
|
||||
|
||||
.. versionadded:: 3.3
|
||||
|
||||
.. deprecated:: 3.14
|
||||
This API does nothing since Python 3.12.
|
||||
Previously, this could be called to check if
|
||||
:c:func:`PyUnicode_READY` is necessary.
|
||||
|
|
|
|||
|
|
@ -348,8 +348,20 @@ the same library that the Python runtime is using.
|
|||
.. versionchanged:: 3.8
|
||||
Added *cf_feature_version* field.
|
||||
|
||||
The available compiler flags are accessible as macros:
|
||||
|
||||
.. c:var:: int CO_FUTURE_DIVISION
|
||||
.. c:namespace:: NULL
|
||||
|
||||
This bit can be set in *flags* to cause division operator ``/`` to be
|
||||
interpreted as "true division" according to :pep:`238`.
|
||||
.. c:macro:: PyCF_ALLOW_TOP_LEVEL_AWAIT
|
||||
PyCF_ONLY_AST
|
||||
PyCF_OPTIMIZED_AST
|
||||
PyCF_TYPE_COMMENTS
|
||||
|
||||
See :ref:`compiler flags <ast-compiler-flags>` in documentation of the
|
||||
:py:mod:`!ast` Python module, which exports these constants under
|
||||
the same names.
|
||||
|
||||
.. c:var:: int CO_FUTURE_DIVISION
|
||||
|
||||
This bit can be set in *flags* to cause division operator ``/`` to be
|
||||
interpreted as "true division" according to :pep:`238`.
|
||||
|
|
|
|||
|
|
@ -88,6 +88,15 @@ as much as it can.
|
|||
Use :c:func:`PyWeakref_GetRef` instead.
|
||||
|
||||
|
||||
.. c:function:: int PyWeakref_IsDead(PyObject *ref)
|
||||
|
||||
Test if the weak reference *ref* is dead. Returns 1 if the reference is
|
||||
dead, 0 if it is alive, and -1 with an error set if *ref* is not a weak
|
||||
reference object.
|
||||
|
||||
.. versionadded:: 3.14
|
||||
|
||||
|
||||
.. c:function:: void PyObject_ClearWeakRefs(PyObject *object)
|
||||
|
||||
This function is called by the :c:member:`~PyTypeObject.tp_dealloc` handler
|
||||
|
|
|
|||
91
Doc/conf.py
91
Doc/conf.py
|
|
@ -6,12 +6,10 @@
|
|||
# The contents of this file are pickled, so don't put values in the namespace
|
||||
# that aren't pickleable (module imports are okay, they're removed automatically).
|
||||
|
||||
import importlib
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
|
||||
import sphinx
|
||||
from importlib import import_module
|
||||
from importlib.util import find_spec
|
||||
|
||||
# Make our custom extensions available to Sphinx
|
||||
sys.path.append(os.path.abspath('tools/extensions'))
|
||||
|
|
@ -28,8 +26,14 @@ extensions = [
|
|||
'audit_events',
|
||||
'availability',
|
||||
'c_annotations',
|
||||
'changes',
|
||||
'glossary_search',
|
||||
'grammar_snippet',
|
||||
'implementation_detail',
|
||||
'issue_role',
|
||||
'lexers',
|
||||
'misc_news',
|
||||
'pydoc_topics',
|
||||
'pyspecific',
|
||||
'sphinx.ext.coverage',
|
||||
'sphinx.ext.doctest',
|
||||
|
|
@ -37,19 +41,17 @@ extensions = [
|
|||
]
|
||||
|
||||
# Skip if downstream redistributors haven't installed them
|
||||
try:
|
||||
import notfound.extension # noqa: F401
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
extensions.append('notfound.extension')
|
||||
try:
|
||||
import sphinxext.opengraph # noqa: F401
|
||||
except ImportError:
|
||||
pass
|
||||
else:
|
||||
extensions.append('sphinxext.opengraph')
|
||||
|
||||
_OPTIONAL_EXTENSIONS = (
|
||||
'notfound.extension',
|
||||
'sphinxext.opengraph',
|
||||
)
|
||||
for optional_ext in _OPTIONAL_EXTENSIONS:
|
||||
try:
|
||||
if find_spec(optional_ext) is not None:
|
||||
extensions.append(optional_ext)
|
||||
except (ImportError, ValueError):
|
||||
pass
|
||||
del _OPTIONAL_EXTENSIONS
|
||||
|
||||
doctest_global_setup = '''
|
||||
try:
|
||||
|
|
@ -72,12 +74,19 @@ copyright = "2001 Python Software Foundation"
|
|||
# We look for the Include/patchlevel.h file in the current Python source tree
|
||||
# and replace the values accordingly.
|
||||
# See Doc/tools/extensions/patchlevel.py
|
||||
version, release = importlib.import_module('patchlevel').get_version_info()
|
||||
version, release = import_module('patchlevel').get_version_info()
|
||||
|
||||
rst_epilog = f"""
|
||||
.. |python_version_literal| replace:: ``Python {version}``
|
||||
.. |python_x_dot_y_literal| replace:: ``python{version}``
|
||||
.. |usr_local_bin_python_x_dot_y_literal| replace:: ``/usr/local/bin/python{version}``
|
||||
|
||||
.. Apparently this how you hack together a formatted link:
|
||||
(https://www.docutils.org/docs/ref/rst/directives.html#replacement-text)
|
||||
.. |FORCE_COLOR| replace:: ``FORCE_COLOR``
|
||||
.. _FORCE_COLOR: https://force-color.org/
|
||||
.. |NO_COLOR| replace:: ``NO_COLOR``
|
||||
.. _NO_COLOR: https://no-color.org/
|
||||
"""
|
||||
|
||||
# There are two options for replacing |today|. Either, you set today to some
|
||||
|
|
@ -90,13 +99,12 @@ today_fmt = '%B %d, %Y'
|
|||
highlight_language = 'python3'
|
||||
|
||||
# Minimum version of sphinx required
|
||||
needs_sphinx = '7.2.6'
|
||||
# Keep this version in sync with ``Doc/requirements.txt``.
|
||||
needs_sphinx = '8.2.0'
|
||||
|
||||
# Create table of contents entries for domain objects (e.g. functions, classes,
|
||||
# attributes, etc.). Default is True.
|
||||
toc_object_entries = True
|
||||
# Hide parents to tidy up long entries in sidebar
|
||||
toc_object_entries_show_parents = 'hide'
|
||||
toc_object_entries = False
|
||||
|
||||
# Ignore any .rst files in the includes/ directory;
|
||||
# they're embedded in pages but not rendered as individual pages.
|
||||
|
|
@ -300,7 +308,6 @@ nitpick_ignore += [
|
|||
('py:attr', '__annotations__'),
|
||||
('py:meth', '__missing__'),
|
||||
('py:attr', '__wrapped__'),
|
||||
('py:attr', 'decimal.Context.clamp'),
|
||||
('py:meth', 'index'), # list.index, tuple.index, etc.
|
||||
]
|
||||
|
||||
|
|
@ -371,13 +378,7 @@ html_context = {
|
|||
|
||||
# This 'Last updated on:' timestamp is inserted at the bottom of every page.
|
||||
html_last_updated_fmt = '%b %d, %Y (%H:%M UTC)'
|
||||
if sphinx.version_info[:2] >= (8, 1):
|
||||
html_last_updated_use_utc = True
|
||||
else:
|
||||
html_time = int(os.environ.get('SOURCE_DATE_EPOCH', time.time()))
|
||||
html_last_updated_fmt = time.strftime(
|
||||
html_last_updated_fmt, time.gmtime(html_time)
|
||||
)
|
||||
html_last_updated_use_utc = True
|
||||
|
||||
# Path to find HTML templates to override theme
|
||||
templates_path = ['tools/templates']
|
||||
|
|
@ -561,8 +562,6 @@ linkcheck_allowed_redirects = {
|
|||
r'https://github.com/python/cpython/tree/.*': 'https://github.com/python/cpython/blob/.*',
|
||||
# Intentional HTTP use at Misc/NEWS.d/3.5.0a1.rst
|
||||
r'http://www.python.org/$': 'https://www.python.org/$',
|
||||
# Used in license page, keep as is
|
||||
r'https://www.zope.org/': r'https://www.zope.dev/',
|
||||
# Microsoft's redirects to learn.microsoft.com
|
||||
r'https://msdn.microsoft.com/.*': 'https://learn.microsoft.com/.*',
|
||||
r'https://docs.microsoft.com/.*': 'https://learn.microsoft.com/.*',
|
||||
|
|
@ -614,16 +613,6 @@ extlinks = {
|
|||
}
|
||||
extlinks_detect_hardcoded_links = True
|
||||
|
||||
if sphinx.version_info[:2] < (8, 1):
|
||||
# Sphinx 8.1 has in-built CVE and CWE roles.
|
||||
extlinks |= {
|
||||
"cve": (
|
||||
"https://www.cve.org/CVERecord?id=CVE-%s",
|
||||
"CVE-%s",
|
||||
),
|
||||
"cwe": ("https://cwe.mitre.org/data/definitions/%s.html", "CWE-%s"),
|
||||
}
|
||||
|
||||
# Options for c_annotations extension
|
||||
# -----------------------------------
|
||||
|
||||
|
|
@ -634,11 +623,19 @@ stable_abi_file = 'data/stable_abi.dat'
|
|||
# Options for sphinxext-opengraph
|
||||
# -------------------------------
|
||||
|
||||
ogp_site_url = 'https://docs.python.org/3/'
|
||||
ogp_canonical_url = 'https://docs.python.org/3/'
|
||||
ogp_site_name = 'Python documentation'
|
||||
ogp_image = '_static/og-image.png'
|
||||
ogp_social_cards = { # Used when matplotlib is installed
|
||||
'image': '_static/og-image.png',
|
||||
'line_color': '#3776ab',
|
||||
}
|
||||
ogp_custom_meta_tags = [
|
||||
'<meta property="og:image:width" content="200" />',
|
||||
'<meta property="og:image:height" content="200" />',
|
||||
'<meta name="theme-color" content="#3776ab" />',
|
||||
'<meta name="theme-color" content="#3776ab">',
|
||||
]
|
||||
if 'create-social-cards' not in tags: # noqa: F821
|
||||
# Define a static preview image when not creating social cards
|
||||
ogp_image = '_static/og-image.png'
|
||||
ogp_custom_meta_tags += [
|
||||
'<meta property="og:image:width" content="200">',
|
||||
'<meta property="og:image:height" content="200">',
|
||||
]
|
||||
|
|
|
|||
|
|
@ -13,14 +13,12 @@ packaging<25
|
|||
Pygments<3
|
||||
requests<3
|
||||
snowballstemmer<3
|
||||
# keep lower-bounds until Sphinx 8.1 is released
|
||||
# https://github.com/sphinx-doc/sphinx/pull/12756
|
||||
sphinxcontrib-applehelp>=1.0.7,<3
|
||||
sphinxcontrib-devhelp>=1.0.6,<3
|
||||
sphinxcontrib-htmlhelp>=2.0.6,<3
|
||||
sphinxcontrib-jsmath>=1.0.1,<2
|
||||
sphinxcontrib-qthelp>=1.0.6,<3
|
||||
sphinxcontrib-serializinghtml>=1.1.9,<3
|
||||
sphinxcontrib-applehelp<3
|
||||
sphinxcontrib-devhelp<3
|
||||
sphinxcontrib-htmlhelp<3
|
||||
sphinxcontrib-jsmath<2
|
||||
sphinxcontrib-qthelp<3
|
||||
sphinxcontrib-serializinghtml<3
|
||||
|
||||
# Direct dependencies of Jinja2 (Jinja is a dependency of Sphinx, see above)
|
||||
MarkupSafe<3
|
||||
|
|
|
|||
|
|
@ -180,7 +180,7 @@ PyCapsule_IsValid:const char*:name::
|
|||
PyCapsule_New:PyObject*::+1:
|
||||
PyCapsule_New:void*:pointer::
|
||||
PyCapsule_New:const char *:name::
|
||||
PyCapsule_New::void (* destructor)(PyObject* )::
|
||||
PyCapsule_New:void (*)(PyObject *):destructor::
|
||||
|
||||
PyCapsule_SetContext:int:::
|
||||
PyCapsule_SetContext:PyObject*:self:0:
|
||||
|
|
@ -349,11 +349,11 @@ PyComplex_CheckExact:int:::
|
|||
PyComplex_CheckExact:PyObject*:p:0:
|
||||
|
||||
PyComplex_FromCComplex:PyObject*::+1:
|
||||
PyComplex_FromCComplex::Py_complex v::
|
||||
PyComplex_FromCComplex:Py_complex:v::
|
||||
|
||||
PyComplex_FromDoubles:PyObject*::+1:
|
||||
PyComplex_FromDoubles::double real::
|
||||
PyComplex_FromDoubles::double imag::
|
||||
PyComplex_FromDoubles:double:real::
|
||||
PyComplex_FromDoubles:double:imag::
|
||||
|
||||
PyComplex_ImagAsDouble:double:::
|
||||
PyComplex_ImagAsDouble:PyObject*:op:0:
|
||||
|
|
@ -620,7 +620,9 @@ PyErr_GetExcInfo:PyObject**:pvalue:+1:
|
|||
PyErr_GetExcInfo:PyObject**:ptraceback:+1:
|
||||
|
||||
PyErr_GetRaisedException:PyObject*::+1:
|
||||
PyErr_SetRaisedException::::
|
||||
|
||||
PyErr_SetRaisedException:void:::
|
||||
PyErr_SetRaisedException:PyObject *:exc:0:stolen
|
||||
|
||||
PyErr_GivenExceptionMatches:int:::
|
||||
PyErr_GivenExceptionMatches:PyObject*:given:0:
|
||||
|
|
@ -640,9 +642,9 @@ PyErr_NewExceptionWithDoc:PyObject*:dict:0:
|
|||
PyErr_NoMemory:PyObject*::null:
|
||||
|
||||
PyErr_NormalizeException:void:::
|
||||
PyErr_NormalizeException:PyObject**:exc::???
|
||||
PyErr_NormalizeException:PyObject**:val::???
|
||||
PyErr_NormalizeException:PyObject**:tb::???
|
||||
PyErr_NormalizeException:PyObject**:exc:+1:???
|
||||
PyErr_NormalizeException:PyObject**:val:+1:???
|
||||
PyErr_NormalizeException:PyObject**:tb:+1:???
|
||||
|
||||
PyErr_Occurred:PyObject*::0:
|
||||
|
||||
|
|
@ -1091,9 +1093,6 @@ PyImport_ImportModuleLevelObject:PyObject*:locals:0:???
|
|||
PyImport_ImportModuleLevelObject:PyObject*:fromlist:0:???
|
||||
PyImport_ImportModuleLevelObject:int:level::
|
||||
|
||||
PyImport_ImportModuleNoBlock:PyObject*::+1:
|
||||
PyImport_ImportModuleNoBlock:const char*:name::
|
||||
|
||||
PyImport_ReloadModule:PyObject*::+1:
|
||||
PyImport_ReloadModule:PyObject*:m:0:
|
||||
|
||||
|
|
@ -1297,6 +1296,13 @@ PyLong_GetSign:int:::
|
|||
PyLong_GetSign:PyObject*:v:0:
|
||||
PyLong_GetSign:int*:sign::
|
||||
|
||||
PyLong_Export:int:::
|
||||
PyLong_Export:PyObject*:obj:0:
|
||||
PyLong_Export:PyLongExport*:export_long::
|
||||
|
||||
PyLongWriter_Finish:PyObject*::+1:
|
||||
PyLongWriter_Finish:PyLongWriter*:writer::
|
||||
|
||||
PyMapping_Check:int:::
|
||||
PyMapping_Check:PyObject*:o:0:
|
||||
|
||||
|
|
@ -1314,7 +1320,7 @@ PyMapping_GetItemString:const char*:key::
|
|||
|
||||
PyMapping_HasKey:int:::
|
||||
PyMapping_HasKey:PyObject*:o:0:
|
||||
PyMapping_HasKey:PyObject*:key::
|
||||
PyMapping_HasKey:PyObject*:key:0:
|
||||
|
||||
PyMapping_HasKeyString:int:::
|
||||
PyMapping_HasKeyString:PyObject*:o:0:
|
||||
|
|
@ -1474,7 +1480,7 @@ PyModule_GetState:void*:::
|
|||
PyModule_GetState:PyObject*:module:0:
|
||||
|
||||
PyModule_New:PyObject*::+1:
|
||||
PyModule_New::char* name::
|
||||
PyModule_New:char*:name::
|
||||
|
||||
PyModule_NewObject:PyObject*::+1:
|
||||
PyModule_NewObject:PyObject*:name:+1:
|
||||
|
|
@ -1484,7 +1490,7 @@ PyModule_SetDocString:PyObject*:module:0:
|
|||
PyModule_SetDocString:const char*:docstring::
|
||||
|
||||
PyModuleDef_Init:PyObject*::0:
|
||||
PyModuleDef_Init:PyModuleDef*:def:0:
|
||||
PyModuleDef_Init:PyModuleDef*:def::
|
||||
|
||||
PyNumber_Absolute:PyObject*::+1:
|
||||
PyNumber_Absolute:PyObject*:o:0:
|
||||
|
|
@ -1847,6 +1853,9 @@ PyObject_RichCompareBool:PyObject*:o1:0:
|
|||
PyObject_RichCompareBool:PyObject*:o2:0:
|
||||
PyObject_RichCompareBool:int:opid::
|
||||
|
||||
PyObject_SelfIter:PyObject*::+1:
|
||||
PyObject_SelfIter:PyObject*:obj:0:
|
||||
|
||||
PyObject_SetAttr:int:::
|
||||
PyObject_SetAttr:PyObject*:o:0:
|
||||
PyObject_SetAttr:PyObject*:attr_name:0:
|
||||
|
|
@ -1984,10 +1993,10 @@ PyRun_StringFlags:PyObject*:locals:0:
|
|||
PyRun_StringFlags:PyCompilerFlags*:flags::
|
||||
|
||||
PySeqIter_Check:int:::
|
||||
PySeqIter_Check::op::
|
||||
PySeqIter_Check:PyObject *:op:0:
|
||||
|
||||
PySeqIter_New:PyObject*::+1:
|
||||
PySeqIter_New:PyObject*:seq::
|
||||
PySeqIter_New:PyObject*:seq:0:
|
||||
|
||||
PySequence_Check:int:::
|
||||
PySequence_Check:PyObject*:o:0:
|
||||
|
|
@ -2421,7 +2430,7 @@ PyUnicode_GET_LENGTH:PyObject*:o:0:
|
|||
PyUnicode_KIND:int:::
|
||||
PyUnicode_KIND:PyObject*:o:0:
|
||||
|
||||
PyUnicode_MAX_CHAR_VALUE::::
|
||||
PyUnicode_MAX_CHAR_VALUE:Py_UCS4:::
|
||||
PyUnicode_MAX_CHAR_VALUE:PyObject*:o:0:
|
||||
|
||||
Py_UNICODE_ISALNUM:int:::
|
||||
|
|
@ -2488,7 +2497,7 @@ PyUnicode_FromWideChar:const wchar_t*:w::
|
|||
PyUnicode_FromWideChar:Py_ssize_t:size::
|
||||
|
||||
PyUnicode_AsWideChar:Py_ssize_t:::
|
||||
PyUnicode_AsWideChar:PyObject*:*unicode:0:
|
||||
PyUnicode_AsWideChar:PyObject*:unicode:0:
|
||||
PyUnicode_AsWideChar:wchar_t*:w::
|
||||
PyUnicode_AsWideChar:Py_ssize_t:size::
|
||||
|
||||
|
|
@ -2541,7 +2550,7 @@ PyUnicode_AsUTF8String:PyObject*:unicode:0:
|
|||
|
||||
PyUnicode_AsUTF8AndSize:const char*:::
|
||||
PyUnicode_AsUTF8AndSize:PyObject*:unicode:0:
|
||||
PyUnicode_AsUTF8AndSize:Py_ssize_t*:size:0:
|
||||
PyUnicode_AsUTF8AndSize:Py_ssize_t*:size::
|
||||
|
||||
PyUnicode_AsUTF8:const char*:::
|
||||
PyUnicode_AsUTF8:PyObject*:unicode:0:
|
||||
|
|
@ -2624,6 +2633,13 @@ PyUnicode_DecodeMBCSStateful:Py_ssize_t:size::
|
|||
PyUnicode_DecodeMBCSStateful:const char*:errors::
|
||||
PyUnicode_DecodeMBCSStateful:Py_ssize_t*:consumed::
|
||||
|
||||
PyUnicode_DecodeCodePageStateful:PyObject*::+1:
|
||||
PyUnicode_DecodeCodePageStateful:int:code_page::
|
||||
PyUnicode_DecodeCodePageStateful:const char*:s::
|
||||
PyUnicode_DecodeCodePageStateful:Py_ssize_t:size::
|
||||
PyUnicode_DecodeCodePageStateful:const char*:errors::
|
||||
PyUnicode_DecodeCodePageStateful:Py_ssize_t*:consumed::
|
||||
|
||||
PyUnicode_EncodeCodePage:PyObject*::+1:
|
||||
PyUnicode_EncodeCodePage:int:code_page::
|
||||
PyUnicode_EncodeCodePage:PyObject*:unicode:0:
|
||||
|
|
@ -2636,13 +2652,26 @@ PyUnicode_Concat:PyObject*::+1:
|
|||
PyUnicode_Concat:PyObject*:left:0:
|
||||
PyUnicode_Concat:PyObject*:right:0:
|
||||
|
||||
PyUnicode_Partition:PyObject*::+1:
|
||||
PyUnicode_Partition:PyObject*:unicode:0:
|
||||
PyUnicode_Partition:PyObject*:sep:0:
|
||||
|
||||
PyUnicode_RPartition:PyObject*::+1:
|
||||
PyUnicode_RPartition:PyObject*:unicode:0:
|
||||
PyUnicode_RPartition:PyObject*:sep:0:
|
||||
|
||||
PyUnicode_RSplit:PyObject*::+1:
|
||||
PyUnicode_RSplit:PyObject*:unicode:0:
|
||||
PyUnicode_RSplit:PyObject*:sep:0:
|
||||
PyUnicode_RSplit:Py_ssize_t:maxsplit::
|
||||
|
||||
PyUnicode_Split:PyObject*::+1:
|
||||
PyUnicode_Split:PyObject*:left:0:
|
||||
PyUnicode_Split:PyObject*:right:0:
|
||||
PyUnicode_Split:PyObject*:unicode:0:
|
||||
PyUnicode_Split:PyObject*:sep:0:
|
||||
PyUnicode_Split:Py_ssize_t:maxsplit::
|
||||
|
||||
PyUnicode_Splitlines:PyObject*::+1:
|
||||
PyUnicode_Splitlines:PyObject*:s:0:
|
||||
PyUnicode_Splitlines:PyObject*:unicode:0:
|
||||
PyUnicode_Splitlines:int:keepend::
|
||||
|
||||
PyUnicode_Translate:PyObject*::+1:
|
||||
|
|
@ -2738,6 +2767,23 @@ PyUnicode_FromFormatV:PyObject*::+1:
|
|||
PyUnicode_FromFormatV:const char*:format::
|
||||
PyUnicode_FromFormatV:va_list:args::
|
||||
|
||||
PyUnicode_FromOrdinal:PyObject*::+1:
|
||||
PyUnicode_FromOrdinal:int:ordinal::
|
||||
|
||||
PyUnicode_Append:void:::
|
||||
PyUnicode_Append:PyObject**:p_left:0:
|
||||
PyUnicode_Append:PyObject*:right::
|
||||
|
||||
PyUnicode_AppendAndDel:void:::
|
||||
PyUnicode_AppendAndDel:PyObject**:p_left:0:
|
||||
PyUnicode_AppendAndDel:PyObject*:right:-1:
|
||||
|
||||
PyUnicode_BuildEncodingMap:PyObject*::+1:
|
||||
PyUnicode_BuildEncodingMap:PyObject*:string:::
|
||||
|
||||
PyUnicode_GetDefaultEncoding:const char*:::
|
||||
PyUnicode_GetDefaultEncoding::void::
|
||||
|
||||
PyUnicode_GetLength:Py_ssize_t:::
|
||||
PyUnicode_GetLength:PyObject*:unicode:0:
|
||||
|
||||
|
|
@ -2748,6 +2794,10 @@ PyUnicode_CopyCharacters:PyObject*:from:0:
|
|||
PyUnicode_CopyCharacters:Py_ssize_t:from_start::
|
||||
PyUnicode_CopyCharacters:Py_ssize_t:how_many::
|
||||
|
||||
PyUnicode_Resize:int:::
|
||||
PyUnicode_Resize:PyObject**:unicode:0:
|
||||
PyUnicode_Resize:Py_ssize_t:length::
|
||||
|
||||
PyUnicode_Fill:Py_ssize_t:::
|
||||
PyUnicode_Fill:PyObject*:unicode:0:
|
||||
PyUnicode_Fill:Py_ssize_t:start::
|
||||
|
|
@ -2864,13 +2914,13 @@ PyUnicodeDecodeError_SetStart:PyObject*:exc:0:
|
|||
PyUnicodeDecodeError_SetStart:Py_ssize_t:start::
|
||||
|
||||
PyWeakref_Check:int:::
|
||||
PyWeakref_Check:PyObject*:ob::
|
||||
PyWeakref_Check:PyObject*:ob:0:
|
||||
|
||||
PyWeakref_CheckProxy:int:::
|
||||
PyWeakref_CheckProxy:PyObject*:ob::
|
||||
PyWeakref_CheckProxy:PyObject*:ob:0:
|
||||
|
||||
PyWeakref_CheckRef:int:::
|
||||
PyWeakref_CheckRef:PyObject*:ob::
|
||||
PyWeakref_CheckRef:PyObject*:ob:0:
|
||||
|
||||
PyWeakref_GET_OBJECT:PyObject*::0:
|
||||
PyWeakref_GET_OBJECT:PyObject*:ref:0:
|
||||
|
|
@ -2957,18 +3007,8 @@ Py_GetCompiler:const char*:::
|
|||
|
||||
Py_GetCopyright:const char*:::
|
||||
|
||||
Py_GetExecPrefix:wchar_t*:::
|
||||
|
||||
Py_GetPath:wchar_t*:::
|
||||
|
||||
Py_GetPlatform:const char*:::
|
||||
|
||||
Py_GetPrefix:wchar_t*:::
|
||||
|
||||
Py_GetProgramFullPath:wchar_t*:::
|
||||
|
||||
Py_GetProgramName:wchar_t*:::
|
||||
|
||||
Py_GetVersion:const char*:::
|
||||
|
||||
Py_INCREF:void:::
|
||||
|
|
@ -3040,3 +3080,11 @@ _Py_c_quot:Py_complex:divisor::
|
|||
_Py_c_sum:Py_complex:::
|
||||
_Py_c_sum:Py_complex:left::
|
||||
_Py_c_sum:Py_complex:right::
|
||||
|
||||
PyImport_ImportModuleAttr:PyObject*::+1:
|
||||
PyImport_ImportModuleAttr:PyObject*:mod_name:0:
|
||||
PyImport_ImportModuleAttr:PyObject*:attr_name:0:
|
||||
|
||||
PyImport_ImportModuleAttrString:PyObject*::+1:
|
||||
PyImport_ImportModuleAttrString:const char *:mod_name::
|
||||
PyImport_ImportModuleAttrString:const char *:attr_name::
|
||||
|
|
|
|||
16
Doc/data/stable_abi.dat
generated
16
Doc/data/stable_abi.dat
generated
|
|
@ -323,7 +323,6 @@ func,PyImport_ImportFrozenModuleObject,3.7,,
|
|||
func,PyImport_ImportModule,3.2,,
|
||||
func,PyImport_ImportModuleLevel,3.2,,
|
||||
func,PyImport_ImportModuleLevelObject,3.7,,
|
||||
func,PyImport_ImportModuleNoBlock,3.2,,
|
||||
func,PyImport_ReloadModule,3.2,,
|
||||
func,PyIndex_Check,3.8,,
|
||||
type,PyInterpreterState,3.2,,opaque
|
||||
|
|
@ -362,6 +361,7 @@ func,PyLong_AsLong,3.2,,
|
|||
func,PyLong_AsLongAndOverflow,3.2,,
|
||||
func,PyLong_AsLongLong,3.2,,
|
||||
func,PyLong_AsLongLongAndOverflow,3.2,,
|
||||
func,PyLong_AsNativeBytes,3.14,,
|
||||
func,PyLong_AsSize_t,3.2,,
|
||||
func,PyLong_AsSsize_t,3.2,,
|
||||
func,PyLong_AsUInt32,3.14,,
|
||||
|
|
@ -376,6 +376,7 @@ func,PyLong_FromInt32,3.14,,
|
|||
func,PyLong_FromInt64,3.14,,
|
||||
func,PyLong_FromLong,3.2,,
|
||||
func,PyLong_FromLongLong,3.2,,
|
||||
func,PyLong_FromNativeBytes,3.14,,
|
||||
func,PyLong_FromSize_t,3.2,,
|
||||
func,PyLong_FromSsize_t,3.2,,
|
||||
func,PyLong_FromString,3.2,,
|
||||
|
|
@ -383,6 +384,7 @@ func,PyLong_FromUInt32,3.14,,
|
|||
func,PyLong_FromUInt64,3.14,,
|
||||
func,PyLong_FromUnsignedLong,3.2,,
|
||||
func,PyLong_FromUnsignedLongLong,3.2,,
|
||||
func,PyLong_FromUnsignedNativeBytes,3.14,,
|
||||
func,PyLong_FromVoidPtr,3.2,,
|
||||
func,PyLong_GetInfo,3.2,,
|
||||
data,PyLong_Type,3.2,,
|
||||
|
|
@ -737,11 +739,7 @@ func,PyUnicode_Append,3.2,,
|
|||
func,PyUnicode_AppendAndDel,3.2,,
|
||||
func,PyUnicode_AsASCIIString,3.2,,
|
||||
func,PyUnicode_AsCharmapString,3.2,,
|
||||
func,PyUnicode_AsDecodedObject,3.2,,
|
||||
func,PyUnicode_AsDecodedUnicode,3.2,,
|
||||
func,PyUnicode_AsEncodedObject,3.2,,
|
||||
func,PyUnicode_AsEncodedString,3.2,,
|
||||
func,PyUnicode_AsEncodedUnicode,3.2,,
|
||||
func,PyUnicode_AsLatin1String,3.2,,
|
||||
func,PyUnicode_AsMBCSString,3.7,on Windows,
|
||||
func,PyUnicode_AsRawUnicodeEscapeString,3.2,,
|
||||
|
|
@ -859,13 +857,7 @@ func,Py_GetCompiler,3.2,,
|
|||
func,Py_GetConstant,3.13,,
|
||||
func,Py_GetConstantBorrowed,3.13,,
|
||||
func,Py_GetCopyright,3.2,,
|
||||
func,Py_GetExecPrefix,3.2,,
|
||||
func,Py_GetPath,3.2,,
|
||||
func,Py_GetPlatform,3.2,,
|
||||
func,Py_GetPrefix,3.2,,
|
||||
func,Py_GetProgramFullPath,3.2,,
|
||||
func,Py_GetProgramName,3.2,,
|
||||
func,Py_GetPythonHome,3.2,,
|
||||
func,Py_GetRecursionLimit,3.2,,
|
||||
func,Py_GetVersion,3.2,,
|
||||
data,Py_HasFileSystemDefaultEncoding,3.2,,
|
||||
|
|
@ -883,6 +875,8 @@ func,Py_Main,3.2,,
|
|||
func,Py_MakePendingCalls,3.2,,
|
||||
func,Py_NewInterpreter,3.2,,
|
||||
func,Py_NewRef,3.10,,
|
||||
func,Py_PACK_FULL_VERSION,3.14,,
|
||||
func,Py_PACK_VERSION,3.14,,
|
||||
func,Py_REFCNT,3.14,,
|
||||
func,Py_ReprEnter,3.2,,
|
||||
func,Py_ReprLeave,3.2,,
|
||||
|
|
|
|||
|
|
@ -6,67 +6,3 @@ Pending removal in Python 3.14
|
|||
|
||||
* Creating :c:data:`immutable types <Py_TPFLAGS_IMMUTABLETYPE>` with mutable
|
||||
bases (:gh:`95388`).
|
||||
|
||||
* Functions to configure Python's initialization, deprecated in Python 3.11:
|
||||
|
||||
* :c:func:`!PySys_SetArgvEx()`:
|
||||
Set :c:member:`PyConfig.argv` instead.
|
||||
* :c:func:`!PySys_SetArgv()`:
|
||||
Set :c:member:`PyConfig.argv` instead.
|
||||
* :c:func:`!Py_SetProgramName()`:
|
||||
Set :c:member:`PyConfig.program_name` instead.
|
||||
* :c:func:`!Py_SetPythonHome()`:
|
||||
Set :c:member:`PyConfig.home` instead.
|
||||
|
||||
The :c:func:`Py_InitializeFromConfig` API should be used with
|
||||
:c:type:`PyConfig` instead.
|
||||
|
||||
* Global configuration variables:
|
||||
|
||||
* :c:var:`Py_DebugFlag`:
|
||||
Use :c:member:`PyConfig.parser_debug` instead.
|
||||
* :c:var:`Py_VerboseFlag`:
|
||||
Use :c:member:`PyConfig.verbose` instead.
|
||||
* :c:var:`Py_QuietFlag`:
|
||||
Use :c:member:`PyConfig.quiet` instead.
|
||||
* :c:var:`Py_InteractiveFlag`:
|
||||
Use :c:member:`PyConfig.interactive` instead.
|
||||
* :c:var:`Py_InspectFlag`:
|
||||
Use :c:member:`PyConfig.inspect` instead.
|
||||
* :c:var:`Py_OptimizeFlag`:
|
||||
Use :c:member:`PyConfig.optimization_level` instead.
|
||||
* :c:var:`Py_NoSiteFlag`:
|
||||
Use :c:member:`PyConfig.site_import` instead.
|
||||
* :c:var:`Py_BytesWarningFlag`:
|
||||
Use :c:member:`PyConfig.bytes_warning` instead.
|
||||
* :c:var:`Py_FrozenFlag`:
|
||||
Use :c:member:`PyConfig.pathconfig_warnings` instead.
|
||||
* :c:var:`Py_IgnoreEnvironmentFlag`:
|
||||
Use :c:member:`PyConfig.use_environment` instead.
|
||||
* :c:var:`Py_DontWriteBytecodeFlag`:
|
||||
Use :c:member:`PyConfig.write_bytecode` instead.
|
||||
* :c:var:`Py_NoUserSiteDirectory`:
|
||||
Use :c:member:`PyConfig.user_site_directory` instead.
|
||||
* :c:var:`Py_UnbufferedStdioFlag`:
|
||||
Use :c:member:`PyConfig.buffered_stdio` instead.
|
||||
* :c:var:`Py_HashRandomizationFlag`:
|
||||
Use :c:member:`PyConfig.use_hash_seed`
|
||||
and :c:member:`PyConfig.hash_seed` instead.
|
||||
* :c:var:`Py_IsolatedFlag`:
|
||||
Use :c:member:`PyConfig.isolated` instead.
|
||||
* :c:var:`Py_LegacyWindowsFSEncodingFlag`:
|
||||
Use :c:member:`PyPreConfig.legacy_windows_fs_encoding` instead.
|
||||
* :c:var:`Py_LegacyWindowsStdioFlag`:
|
||||
Use :c:member:`PyConfig.legacy_windows_stdio` instead.
|
||||
* :c:var:`!Py_FileSystemDefaultEncoding`:
|
||||
Use :c:member:`PyConfig.filesystem_encoding` instead.
|
||||
* :c:var:`!Py_HasFileSystemDefaultEncoding`:
|
||||
Use :c:member:`PyConfig.filesystem_encoding` instead.
|
||||
* :c:var:`!Py_FileSystemDefaultEncodeErrors`:
|
||||
Use :c:member:`PyConfig.filesystem_errors` instead.
|
||||
* :c:var:`!Py_UTF8Mode`:
|
||||
Use :c:member:`PyPreConfig.utf8_mode` instead.
|
||||
(see :c:func:`Py_PreInitialize`)
|
||||
|
||||
The :c:func:`Py_InitializeFromConfig` API should be used with
|
||||
:c:type:`PyConfig` instead.
|
||||
|
|
|
|||
|
|
@ -2,26 +2,135 @@ Pending removal in Python 3.15
|
|||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
* The bundled copy of ``libmpdecimal``.
|
||||
* The :c:func:`PyImport_ImportModuleNoBlock`:
|
||||
* The :c:func:`!PyImport_ImportModuleNoBlock`:
|
||||
Use :c:func:`PyImport_ImportModule` instead.
|
||||
* :c:func:`PyWeakref_GetObject` and :c:func:`PyWeakref_GET_OBJECT`:
|
||||
Use :c:func:`PyWeakref_GetRef` instead.
|
||||
Use :c:func:`PyWeakref_GetRef` instead. The `pythoncapi-compat project
|
||||
<https://github.com/python/pythoncapi-compat/>`__ can be used to get
|
||||
:c:func:`PyWeakref_GetRef` on Python 3.12 and older.
|
||||
* :c:type:`Py_UNICODE` type and the :c:macro:`!Py_UNICODE_WIDE` macro:
|
||||
Use :c:type:`wchar_t` instead.
|
||||
* Python initialization functions:
|
||||
* :c:func:`!PyUnicode_AsDecodedObject`:
|
||||
Use :c:func:`PyCodec_Decode` instead.
|
||||
* :c:func:`!PyUnicode_AsDecodedUnicode`:
|
||||
Use :c:func:`PyCodec_Decode` instead; Note that some codecs (for example, "base64")
|
||||
may return a type other than :class:`str`, such as :class:`bytes`.
|
||||
* :c:func:`!PyUnicode_AsEncodedObject`:
|
||||
Use :c:func:`PyCodec_Encode` instead.
|
||||
* :c:func:`!PyUnicode_AsEncodedUnicode`:
|
||||
Use :c:func:`PyCodec_Encode` instead; Note that some codecs (for example, "base64")
|
||||
may return a type other than :class:`bytes`, such as :class:`str`.
|
||||
* Python initialization functions, deprecated in Python 3.13:
|
||||
|
||||
* :c:func:`!Py_GetPath`:
|
||||
Use :c:func:`PyConfig_Get("module_search_paths") <PyConfig_Get>`
|
||||
(:data:`sys.path`) instead.
|
||||
* :c:func:`!Py_GetPrefix`:
|
||||
Use :c:func:`PyConfig_Get("base_prefix") <PyConfig_Get>`
|
||||
(:data:`sys.base_prefix`) instead. Use :c:func:`PyConfig_Get("prefix")
|
||||
<PyConfig_Get>` (:data:`sys.prefix`) if :ref:`virtual environments
|
||||
<venv-def>` need to be handled.
|
||||
* :c:func:`!Py_GetExecPrefix`:
|
||||
Use :c:func:`PyConfig_Get("base_exec_prefix") <PyConfig_Get>`
|
||||
(:data:`sys.base_exec_prefix`) instead. Use
|
||||
:c:func:`PyConfig_Get("exec_prefix") <PyConfig_Get>`
|
||||
(:data:`sys.exec_prefix`) if :ref:`virtual environments <venv-def>` need to
|
||||
be handled.
|
||||
* :c:func:`!Py_GetProgramFullPath`:
|
||||
Use :c:func:`PyConfig_Get("executable") <PyConfig_Get>`
|
||||
(:data:`sys.executable`) instead.
|
||||
* :c:func:`!Py_GetProgramName`:
|
||||
Use :c:func:`PyConfig_Get("executable") <PyConfig_Get>`
|
||||
(:data:`sys.executable`) instead.
|
||||
* :c:func:`!Py_GetPythonHome`:
|
||||
Use :c:func:`PyConfig_Get("home") <PyConfig_Get>` or the
|
||||
:envvar:`PYTHONHOME` environment variable instead.
|
||||
|
||||
The `pythoncapi-compat project
|
||||
<https://github.com/python/pythoncapi-compat/>`__ can be used to get
|
||||
:c:func:`PyConfig_Get` on Python 3.13 and older.
|
||||
|
||||
* Functions to configure Python's initialization, deprecated in Python 3.11:
|
||||
|
||||
* :c:func:`!PySys_SetArgvEx()`:
|
||||
Set :c:member:`PyConfig.argv` instead.
|
||||
* :c:func:`!PySys_SetArgv()`:
|
||||
Set :c:member:`PyConfig.argv` instead.
|
||||
* :c:func:`!Py_SetProgramName()`:
|
||||
Set :c:member:`PyConfig.program_name` instead.
|
||||
* :c:func:`!Py_SetPythonHome()`:
|
||||
Set :c:member:`PyConfig.home` instead.
|
||||
* :c:func:`PySys_ResetWarnOptions`:
|
||||
Clear :data:`sys.warnoptions` and :data:`!warnings.filters` instead.
|
||||
* :c:func:`Py_GetExecPrefix`:
|
||||
Get :data:`sys.base_exec_prefix` and :data:`sys.exec_prefix` instead.
|
||||
* :c:func:`Py_GetPath`:
|
||||
Get :data:`sys.path` instead.
|
||||
* :c:func:`Py_GetPrefix`:
|
||||
Get :data:`sys.base_prefix` and :data:`sys.prefix` instead.
|
||||
* :c:func:`Py_GetProgramFullPath`:
|
||||
Get :data:`sys.executable` instead.
|
||||
* :c:func:`Py_GetProgramName`:
|
||||
Get :data:`sys.executable` instead.
|
||||
* :c:func:`Py_GetPythonHome`:
|
||||
Get :c:member:`PyConfig.home`
|
||||
or the :envvar:`PYTHONHOME` environment variable instead.
|
||||
|
||||
The :c:func:`Py_InitializeFromConfig` API should be used with
|
||||
:c:type:`PyConfig` instead.
|
||||
|
||||
* Global configuration variables:
|
||||
|
||||
* :c:var:`Py_DebugFlag`:
|
||||
Use :c:member:`PyConfig.parser_debug` or
|
||||
:c:func:`PyConfig_Get("parser_debug") <PyConfig_Get>` instead.
|
||||
* :c:var:`Py_VerboseFlag`:
|
||||
Use :c:member:`PyConfig.verbose` or
|
||||
:c:func:`PyConfig_Get("verbose") <PyConfig_Get>` instead.
|
||||
* :c:var:`Py_QuietFlag`:
|
||||
Use :c:member:`PyConfig.quiet` or
|
||||
:c:func:`PyConfig_Get("quiet") <PyConfig_Get>` instead.
|
||||
* :c:var:`Py_InteractiveFlag`:
|
||||
Use :c:member:`PyConfig.interactive` or
|
||||
:c:func:`PyConfig_Get("interactive") <PyConfig_Get>` instead.
|
||||
* :c:var:`Py_InspectFlag`:
|
||||
Use :c:member:`PyConfig.inspect` or
|
||||
:c:func:`PyConfig_Get("inspect") <PyConfig_Get>` instead.
|
||||
* :c:var:`Py_OptimizeFlag`:
|
||||
Use :c:member:`PyConfig.optimization_level` or
|
||||
:c:func:`PyConfig_Get("optimization_level") <PyConfig_Get>` instead.
|
||||
* :c:var:`Py_NoSiteFlag`:
|
||||
Use :c:member:`PyConfig.site_import` or
|
||||
:c:func:`PyConfig_Get("site_import") <PyConfig_Get>` instead.
|
||||
* :c:var:`Py_BytesWarningFlag`:
|
||||
Use :c:member:`PyConfig.bytes_warning` or
|
||||
:c:func:`PyConfig_Get("bytes_warning") <PyConfig_Get>` instead.
|
||||
* :c:var:`Py_FrozenFlag`:
|
||||
Use :c:member:`PyConfig.pathconfig_warnings` or
|
||||
:c:func:`PyConfig_Get("pathconfig_warnings") <PyConfig_Get>` instead.
|
||||
* :c:var:`Py_IgnoreEnvironmentFlag`:
|
||||
Use :c:member:`PyConfig.use_environment` or
|
||||
:c:func:`PyConfig_Get("use_environment") <PyConfig_Get>` instead.
|
||||
* :c:var:`Py_DontWriteBytecodeFlag`:
|
||||
Use :c:member:`PyConfig.write_bytecode` or
|
||||
:c:func:`PyConfig_Get("write_bytecode") <PyConfig_Get>` instead.
|
||||
* :c:var:`Py_NoUserSiteDirectory`:
|
||||
Use :c:member:`PyConfig.user_site_directory` or
|
||||
:c:func:`PyConfig_Get("user_site_directory") <PyConfig_Get>` instead.
|
||||
* :c:var:`Py_UnbufferedStdioFlag`:
|
||||
Use :c:member:`PyConfig.buffered_stdio` or
|
||||
:c:func:`PyConfig_Get("buffered_stdio") <PyConfig_Get>` instead.
|
||||
* :c:var:`Py_HashRandomizationFlag`:
|
||||
Use :c:member:`PyConfig.use_hash_seed`
|
||||
and :c:member:`PyConfig.hash_seed` or
|
||||
:c:func:`PyConfig_Get("hash_seed") <PyConfig_Get>` instead.
|
||||
* :c:var:`Py_IsolatedFlag`:
|
||||
Use :c:member:`PyConfig.isolated` or
|
||||
:c:func:`PyConfig_Get("isolated") <PyConfig_Get>` instead.
|
||||
* :c:var:`Py_LegacyWindowsFSEncodingFlag`:
|
||||
Use :c:member:`PyPreConfig.legacy_windows_fs_encoding` or
|
||||
:c:func:`PyConfig_Get("legacy_windows_fs_encoding") <PyConfig_Get>` instead.
|
||||
* :c:var:`Py_LegacyWindowsStdioFlag`:
|
||||
Use :c:member:`PyConfig.legacy_windows_stdio` or
|
||||
:c:func:`PyConfig_Get("legacy_windows_stdio") <PyConfig_Get>` instead.
|
||||
* :c:var:`!Py_FileSystemDefaultEncoding`, :c:var:`!Py_HasFileSystemDefaultEncoding`:
|
||||
Use :c:member:`PyConfig.filesystem_encoding` or
|
||||
:c:func:`PyConfig_Get("filesystem_encoding") <PyConfig_Get>` instead.
|
||||
* :c:var:`!Py_FileSystemDefaultEncodeErrors`:
|
||||
Use :c:member:`PyConfig.filesystem_errors` or
|
||||
:c:func:`PyConfig_Get("filesystem_errors") <PyConfig_Get>` instead.
|
||||
* :c:var:`!Py_UTF8Mode`:
|
||||
Use :c:member:`PyPreConfig.utf8_mode` or
|
||||
:c:func:`PyConfig_Get("utf8_mode") <PyConfig_Get>` instead.
|
||||
(see :c:func:`Py_PreInitialize`)
|
||||
|
||||
The :c:func:`Py_InitializeFromConfig` API should be used with
|
||||
:c:type:`PyConfig` to set these options. Or :c:func:`PyConfig_Get` can be
|
||||
used to get these options at runtime.
|
||||
|
|
|
|||
45
Doc/deprecations/c-api-pending-removal-in-3.18.rst
Normal file
45
Doc/deprecations/c-api-pending-removal-in-3.18.rst
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
Pending removal in Python 3.18
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
* Deprecated private functions (:gh:`128863`):
|
||||
|
||||
* :c:func:`!_PyBytes_Join`: use :c:func:`PyBytes_Join`.
|
||||
* :c:func:`!_PyDict_GetItemStringWithError`: use :c:func:`PyDict_GetItemStringRef`.
|
||||
* :c:func:`!_PyDict_Pop()`: :c:func:`PyDict_Pop`.
|
||||
* :c:func:`!_PyLong_Sign()`: use :c:func:`PyLong_GetSign`.
|
||||
* :c:func:`!_PyLong_FromDigits` and :c:func:`!_PyLong_New`:
|
||||
use :c:func:`PyLongWriter_Create`.
|
||||
* :c:func:`!_PyThreadState_UncheckedGet`: use :c:func:`PyThreadState_GetUnchecked`.
|
||||
* :c:func:`!_PyUnicode_AsString`: use :c:func:`PyUnicode_AsUTF8`.
|
||||
* :c:func:`!_PyUnicodeWriter_Init`:
|
||||
replace ``_PyUnicodeWriter_Init(&writer)`` with
|
||||
:c:func:`writer = PyUnicodeWriter_Create(0) <PyUnicodeWriter_Create>`.
|
||||
* :c:func:`!_PyUnicodeWriter_Finish`:
|
||||
replace ``_PyUnicodeWriter_Finish(&writer)`` with
|
||||
:c:func:`PyUnicodeWriter_Finish(writer) <PyUnicodeWriter_Finish>`.
|
||||
* :c:func:`!_PyUnicodeWriter_Dealloc`:
|
||||
replace ``_PyUnicodeWriter_Dealloc(&writer)`` with
|
||||
:c:func:`PyUnicodeWriter_Discard(writer) <PyUnicodeWriter_Discard>`.
|
||||
* :c:func:`!_PyUnicodeWriter_WriteChar`:
|
||||
replace ``_PyUnicodeWriter_WriteChar(&writer, ch)`` with
|
||||
:c:func:`PyUnicodeWriter_WriteChar(writer, ch) <PyUnicodeWriter_WriteChar>`.
|
||||
* :c:func:`!_PyUnicodeWriter_WriteStr`:
|
||||
replace ``_PyUnicodeWriter_WriteStr(&writer, str)`` with
|
||||
:c:func:`PyUnicodeWriter_WriteStr(writer, str) <PyUnicodeWriter_WriteStr>`.
|
||||
* :c:func:`!_PyUnicodeWriter_WriteSubstring`:
|
||||
replace ``_PyUnicodeWriter_WriteSubstring(&writer, str, start, end)`` with
|
||||
:c:func:`PyUnicodeWriter_WriteSubstring(writer, str, start, end) <PyUnicodeWriter_WriteSubstring>`.
|
||||
* :c:func:`!_PyUnicodeWriter_WriteASCIIString`:
|
||||
replace ``_PyUnicodeWriter_WriteASCIIString(&writer, str)`` with
|
||||
:c:func:`PyUnicodeWriter_WriteUTF8(writer, str) <PyUnicodeWriter_WriteUTF8>`.
|
||||
* :c:func:`!_PyUnicodeWriter_WriteLatin1String`:
|
||||
replace ``_PyUnicodeWriter_WriteLatin1String(&writer, str)`` with
|
||||
:c:func:`PyUnicodeWriter_WriteUTF8(writer, str) <PyUnicodeWriter_WriteUTF8>`.
|
||||
* :c:func:`!_PyUnicodeWriter_Prepare`: (no replacement).
|
||||
* :c:func:`!_PyUnicodeWriter_PrepareKind`: (no replacement).
|
||||
* :c:func:`!_Py_HashPointer`: use :c:func:`Py_HashPointer`.
|
||||
* :c:func:`!_Py_fopen_obj`: use :c:func:`Py_fopen`.
|
||||
|
||||
The `pythoncapi-compat project
|
||||
<https://github.com/python/pythoncapi-compat/>`__ can be used to get these
|
||||
new public functions on Python 3.13 and older.
|
||||
|
|
@ -18,14 +18,6 @@ although there is currently no date scheduled for their removal.
|
|||
Use :c:func:`PyOS_AfterFork_Child` instead.
|
||||
* :c:func:`PySlice_GetIndicesEx`:
|
||||
Use :c:func:`PySlice_Unpack` and :c:func:`PySlice_AdjustIndices` instead.
|
||||
* :c:func:`!PyUnicode_AsDecodedObject`:
|
||||
Use :c:func:`PyCodec_Decode` instead.
|
||||
* :c:func:`!PyUnicode_AsDecodedUnicode`:
|
||||
Use :c:func:`PyCodec_Decode` instead.
|
||||
* :c:func:`!PyUnicode_AsEncodedObject`:
|
||||
Use :c:func:`PyCodec_Encode` instead.
|
||||
* :c:func:`!PyUnicode_AsEncodedUnicode`:
|
||||
Use :c:func:`PyCodec_Encode` instead.
|
||||
* :c:func:`PyUnicode_READY`:
|
||||
Unneeded since Python 3.12
|
||||
* :c:func:`!PyErr_Display`:
|
||||
|
|
@ -34,7 +26,6 @@ although there is currently no date scheduled for their removal.
|
|||
Use :c:func:`!_PyErr_ChainExceptions1` instead.
|
||||
* :c:member:`!PyBytesObject.ob_shash` member:
|
||||
call :c:func:`PyObject_Hash` instead.
|
||||
* :c:member:`!PyDictObject.ma_version_tag` member.
|
||||
* Thread Local Storage (TLS) API:
|
||||
|
||||
* :c:func:`PyThread_create_key`:
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@ Deprecations
|
|||
|
||||
.. include:: pending-removal-in-3.16.rst
|
||||
|
||||
.. include:: pending-removal-in-3.17.rst
|
||||
|
||||
.. include:: pending-removal-in-3.19.rst
|
||||
|
||||
.. include:: pending-removal-in-future.rst
|
||||
|
||||
C API deprecations
|
||||
|
|
@ -12,4 +16,6 @@ C API deprecations
|
|||
|
||||
.. include:: c-api-pending-removal-in-3.15.rst
|
||||
|
||||
.. include:: c-api-pending-removal-in-3.18.rst
|
||||
|
||||
.. include:: c-api-pending-removal-in-future.rst
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ Pending removal in Python 3.14
|
|||
:meth:`~pathlib.PurePath.relative_to`: passing additional arguments is
|
||||
deprecated.
|
||||
|
||||
* :mod:`pkgutil`: :func:`!pkgutil.find_loader` and :func:!pkgutil.get_loader`
|
||||
* :mod:`pkgutil`: :func:`!pkgutil.find_loader` and :func:`!pkgutil.get_loader`
|
||||
now raise :exc:`DeprecationWarning`;
|
||||
use :func:`importlib.util.find_spec` instead.
|
||||
(Contributed by Nikita Sobolev in :gh:`97850`.)
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ Pending removal in Python 3.15
|
|||
|
||||
* :mod:`http.server`:
|
||||
|
||||
* The obsolete and rarely used :class:`~http.server.CGIHTTPRequestHandler`
|
||||
* The obsolete and rarely used :class:`!CGIHTTPRequestHandler`
|
||||
has been deprecated since Python 3.13.
|
||||
No direct replacement exists.
|
||||
*Anything* is better than CGI to interface
|
||||
|
|
@ -29,6 +29,10 @@ Pending removal in Python 3.15
|
|||
* The :option:`!--cgi` flag to the :program:`python -m http.server`
|
||||
command-line interface has been deprecated since Python 3.13.
|
||||
|
||||
* :mod:`importlib`:
|
||||
|
||||
* ``load_module()`` method: use ``exec_module()`` instead.
|
||||
|
||||
* :class:`locale`:
|
||||
|
||||
* The :func:`~locale.getdefaultlocale` function
|
||||
|
|
@ -47,10 +51,15 @@ Pending removal in Python 3.15
|
|||
|
||||
* :mod:`platform`:
|
||||
|
||||
* :func:`~platform.java_ver` has been deprecated since Python 3.13.
|
||||
* :func:`!platform.java_ver` has been deprecated since Python 3.13.
|
||||
This function is only useful for Jython support, has a confusing API,
|
||||
and is largely untested.
|
||||
|
||||
* :mod:`sysconfig`:
|
||||
|
||||
* The *check_home* argument of :func:`sysconfig.is_python_build` has been
|
||||
deprecated since Python 3.12.
|
||||
|
||||
* :mod:`threading`:
|
||||
|
||||
* :func:`~threading.RLock` will take no arguments in Python 3.15.
|
||||
|
|
@ -76,6 +85,13 @@ Pending removal in Python 3.15
|
|||
has been deprecated since Python 3.13.
|
||||
Use the class-based syntax or the functional syntax instead.
|
||||
|
||||
* When using the functional syntax of :class:`~typing.TypedDict`\s, failing
|
||||
to pass a value to the *fields* parameter (``TD = TypedDict("TD")``) or
|
||||
passing ``None`` (``TD = TypedDict("TD", None)``) has been deprecated
|
||||
since Python 3.13.
|
||||
Use ``class TD(TypedDict): pass`` or ``TD = TypedDict("TD", {})``
|
||||
to create a TypedDict with zero field.
|
||||
|
||||
* The :func:`typing.no_type_check_decorator` decorator function
|
||||
has been deprecated since Python 3.13.
|
||||
After eight years in the :mod:`typing` module,
|
||||
|
|
@ -83,7 +99,12 @@ Pending removal in Python 3.15
|
|||
|
||||
* :mod:`wave`:
|
||||
|
||||
* The :meth:`~wave.Wave_read.getmark`, :meth:`!setmark`,
|
||||
and :meth:`~wave.Wave_read.getmarkers` methods of
|
||||
* The ``getmark()``, ``setmark()`` and ``getmarkers()`` methods of
|
||||
the :class:`~wave.Wave_read` and :class:`~wave.Wave_write` classes
|
||||
have been deprecated since Python 3.13.
|
||||
|
||||
* :mod:`zipimport`:
|
||||
|
||||
* :meth:`~zipimport.zipimporter.load_module` has been deprecated since
|
||||
Python 3.10. Use :meth:`~zipimport.zipimporter.exec_module` instead.
|
||||
(Contributed by Jiahao Li in :gh:`125746`.)
|
||||
|
|
|
|||
|
|
@ -19,10 +19,34 @@ Pending removal in Python 3.16
|
|||
* :mod:`asyncio`:
|
||||
|
||||
* :func:`!asyncio.iscoroutinefunction` is deprecated
|
||||
and will be removed in Python 3.16,
|
||||
and will be removed in Python 3.16;
|
||||
use :func:`inspect.iscoroutinefunction` instead.
|
||||
(Contributed by Jiahao Li and Kumar Aditya in :gh:`122875`.)
|
||||
|
||||
* :mod:`asyncio` policy system is deprecated and will be removed in Python 3.16.
|
||||
In particular, the following classes and functions are deprecated:
|
||||
|
||||
* :class:`asyncio.AbstractEventLoopPolicy`
|
||||
* :class:`asyncio.DefaultEventLoopPolicy`
|
||||
* :class:`asyncio.WindowsSelectorEventLoopPolicy`
|
||||
* :class:`asyncio.WindowsProactorEventLoopPolicy`
|
||||
* :func:`asyncio.get_event_loop_policy`
|
||||
* :func:`asyncio.set_event_loop_policy`
|
||||
|
||||
Users should use :func:`asyncio.run` or :class:`asyncio.Runner` with
|
||||
*loop_factory* to use the desired event loop implementation.
|
||||
|
||||
For example, to use :class:`asyncio.SelectorEventLoop` on Windows::
|
||||
|
||||
import asyncio
|
||||
|
||||
async def main():
|
||||
...
|
||||
|
||||
asyncio.run(main(), loop_factory=asyncio.SelectorEventLoop)
|
||||
|
||||
(Contributed by Kumar Aditya in :gh:`127949`.)
|
||||
|
||||
* :mod:`builtins`:
|
||||
|
||||
* Bitwise inversion on boolean types, ``~True`` or ``~False``
|
||||
|
|
@ -32,6 +56,25 @@ Pending removal in Python 3.16
|
|||
In the rare case that you need the bitwise inversion of
|
||||
the underlying integer, convert to ``int`` explicitly (``~int(x)``).
|
||||
|
||||
* :mod:`functools`:
|
||||
|
||||
* Calling the Python implementation of :func:`functools.reduce` with *function*
|
||||
or *sequence* as keyword arguments has been deprecated since Python 3.14.
|
||||
|
||||
* :mod:`logging`:
|
||||
|
||||
Support for custom logging handlers with the *strm* argument is deprecated
|
||||
and scheduled for removal in Python 3.16. Define handlers with the *stream*
|
||||
argument instead. (Contributed by Mariusz Felisiak in :gh:`115032`.)
|
||||
|
||||
* :mod:`mimetypes`:
|
||||
|
||||
* Valid extensions start with a '.' or are empty for
|
||||
:meth:`mimetypes.MimeTypes.add_type`.
|
||||
Undotted extensions are deprecated and will
|
||||
raise a :exc:`ValueError` in Python 3.16.
|
||||
(Contributed by Hugo van Kemenade in :gh:`75223`.)
|
||||
|
||||
* :mod:`shutil`:
|
||||
|
||||
* The :class:`!ExecError` exception
|
||||
|
|
@ -50,6 +93,12 @@ Pending removal in Python 3.16
|
|||
has been deprecated since Python 3.13.
|
||||
Use the :envvar:`PYTHONLEGACYWINDOWSFSENCODING` environment variable instead.
|
||||
|
||||
* :mod:`sysconfig`:
|
||||
|
||||
* The :func:`!sysconfig.expand_makefile_vars` function
|
||||
has been deprecated since Python 3.14.
|
||||
Use the ``vars`` argument of :func:`sysconfig.get_paths` instead.
|
||||
|
||||
* :mod:`tarfile`:
|
||||
|
||||
* The undocumented and unused :attr:`!TarFile.tarfile` attribute
|
||||
|
|
|
|||
10
Doc/deprecations/pending-removal-in-3.17.rst
Normal file
10
Doc/deprecations/pending-removal-in-3.17.rst
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
Pending removal in Python 3.17
|
||||
------------------------------
|
||||
|
||||
* :mod:`typing`:
|
||||
|
||||
- Before Python 3.14, old-style unions were implemented using the private class
|
||||
``typing._UnionGenericAlias``. This class is no longer needed for the implementation,
|
||||
but it has been retained for backward compatibility, with removal scheduled for Python
|
||||
3.17. Users should use documented introspection helpers like :func:`typing.get_origin`
|
||||
and :func:`typing.get_args` instead of relying on private implementation details.
|
||||
8
Doc/deprecations/pending-removal-in-3.19.rst
Normal file
8
Doc/deprecations/pending-removal-in-3.19.rst
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
Pending removal in Python 3.19
|
||||
------------------------------
|
||||
|
||||
* :mod:`ctypes`:
|
||||
|
||||
* Implicitly switching to the MSVC-compatible struct layout by setting
|
||||
:attr:`~ctypes.Structure._pack_` but not :attr:`~ctypes.Structure._layout_`
|
||||
on non-Windows platforms.
|
||||
|
|
@ -13,8 +13,6 @@ although there is currently no date scheduled for their removal.
|
|||
deprecated.
|
||||
* The :class:`argparse.FileType` type converter is deprecated.
|
||||
|
||||
* :mod:`array`'s ``'u'`` format code (:gh:`57281`)
|
||||
|
||||
* :mod:`builtins`:
|
||||
|
||||
* ``bool(NotImplemented)``.
|
||||
|
|
@ -49,6 +47,8 @@ although there is currently no date scheduled for their removal.
|
|||
:data:`calendar.FEBRUARY`.
|
||||
(Contributed by Prince Roshan in :gh:`103636`.)
|
||||
|
||||
* :mod:`codecs`: use :func:`open` instead of :func:`codecs.open`. (:gh:`133038`)
|
||||
|
||||
* :attr:`codeobject.co_lnotab`: use the :meth:`codeobject.co_lines` method
|
||||
instead.
|
||||
|
||||
|
|
@ -63,7 +63,6 @@ although there is currently no date scheduled for their removal.
|
|||
|
||||
* :mod:`importlib`:
|
||||
|
||||
* ``load_module()`` method: use ``exec_module()`` instead.
|
||||
* :func:`~importlib.util.cache_from_source` *debug_override* parameter is
|
||||
deprecated: use the *optimization* parameter instead.
|
||||
|
||||
|
|
@ -112,9 +111,6 @@ although there is currently no date scheduled for their removal.
|
|||
* ``ssl.TLSVersion.TLSv1``
|
||||
* ``ssl.TLSVersion.TLSv1_1``
|
||||
|
||||
* :func:`sysconfig.is_python_build` *check_home* parameter is deprecated and
|
||||
ignored.
|
||||
|
||||
* :mod:`threading` methods:
|
||||
|
||||
* :meth:`!threading.Condition.notifyAll`: use :meth:`~threading.Condition.notify_all`.
|
||||
|
|
@ -128,6 +124,11 @@ although there is currently no date scheduled for their removal.
|
|||
|
||||
* :class:`typing.Text` (:gh:`92332`).
|
||||
|
||||
* The internal class ``typing._UnionGenericAlias`` is no longer used to implement
|
||||
:class:`typing.Union`. To preserve compatibility with users using this private
|
||||
class, a compatibility shim will be provided until at least Python 3.17. (Contributed by
|
||||
Jelle Zijlstra in :gh:`105499`.)
|
||||
|
||||
* :class:`unittest.IsolatedAsyncioTestCase`: it is deprecated to return a value
|
||||
that is not ``None`` from a test case.
|
||||
|
||||
|
|
@ -153,5 +154,5 @@ although there is currently no date scheduled for their removal.
|
|||
will always return ``True``. Prefer explicit ``len(elem)`` or
|
||||
``elem is not None`` tests instead.
|
||||
|
||||
* :meth:`zipimport.zipimporter.load_module` is deprecated:
|
||||
use :meth:`~zipimport.zipimporter.exec_module` instead.
|
||||
* :func:`sys._clear_type_cache` is deprecated:
|
||||
use :func:`sys._clear_internal_caches` instead.
|
||||
|
|
|
|||
|
|
@ -196,8 +196,8 @@ interesting part with respect to embedding Python starts with ::
|
|||
|
||||
After initializing the interpreter, the script is loaded using
|
||||
:c:func:`PyImport_Import`. This routine needs a Python string as its argument,
|
||||
which is constructed using the :c:func:`PyUnicode_FromString` data conversion
|
||||
routine. ::
|
||||
which is constructed using the :c:func:`PyUnicode_DecodeFSDefault` data
|
||||
conversion routine. ::
|
||||
|
||||
pFunc = PyObject_GetAttrString(pModule, argv[2]);
|
||||
/* pFunc is a new reference */
|
||||
|
|
|
|||
|
|
@ -70,22 +70,24 @@ object itself needs to be freed here as well. Here is an example of this
|
|||
function::
|
||||
|
||||
static void
|
||||
newdatatype_dealloc(newdatatypeobject *obj)
|
||||
newdatatype_dealloc(PyObject *op)
|
||||
{
|
||||
free(obj->obj_UnderlyingDatatypePtr);
|
||||
Py_TYPE(obj)->tp_free((PyObject *)obj);
|
||||
newdatatypeobject *self = (newdatatypeobject *) op;
|
||||
free(self->obj_UnderlyingDatatypePtr);
|
||||
Py_TYPE(self)->tp_free(self);
|
||||
}
|
||||
|
||||
If your type supports garbage collection, the destructor should call
|
||||
:c:func:`PyObject_GC_UnTrack` before clearing any member fields::
|
||||
|
||||
static void
|
||||
newdatatype_dealloc(newdatatypeobject *obj)
|
||||
newdatatype_dealloc(PyObject *op)
|
||||
{
|
||||
PyObject_GC_UnTrack(obj);
|
||||
Py_CLEAR(obj->other_obj);
|
||||
newdatatypeobject *self = (newdatatypeobject *) op;
|
||||
PyObject_GC_UnTrack(op);
|
||||
Py_CLEAR(self->other_obj);
|
||||
...
|
||||
Py_TYPE(obj)->tp_free((PyObject *)obj);
|
||||
Py_TYPE(self)->tp_free(self);
|
||||
}
|
||||
|
||||
.. index::
|
||||
|
|
@ -117,17 +119,19 @@ done. This can be done using the :c:func:`PyErr_Fetch` and
|
|||
PyErr_Fetch(&err_type, &err_value, &err_traceback);
|
||||
|
||||
cbresult = PyObject_CallNoArgs(self->my_callback);
|
||||
if (cbresult == NULL)
|
||||
PyErr_WriteUnraisable(self->my_callback);
|
||||
else
|
||||
if (cbresult == NULL) {
|
||||
PyErr_WriteUnraisable(self->my_callback);
|
||||
}
|
||||
else {
|
||||
Py_DECREF(cbresult);
|
||||
}
|
||||
|
||||
/* This restores the saved exception state */
|
||||
PyErr_Restore(err_type, err_value, err_traceback);
|
||||
|
||||
Py_DECREF(self->my_callback);
|
||||
}
|
||||
Py_TYPE(obj)->tp_free((PyObject*)self);
|
||||
Py_TYPE(self)->tp_free(self);
|
||||
}
|
||||
|
||||
.. note::
|
||||
|
|
@ -168,10 +172,11 @@ representation of the instance for which it is called. Here is a simple
|
|||
example::
|
||||
|
||||
static PyObject *
|
||||
newdatatype_repr(newdatatypeobject *obj)
|
||||
newdatatype_repr(PyObject *op)
|
||||
{
|
||||
newdatatypeobject *self = (newdatatypeobject *) op;
|
||||
return PyUnicode_FromFormat("Repr-ified_newdatatype{{size:%d}}",
|
||||
obj->obj_UnderlyingDatatypePtr->size);
|
||||
self->obj_UnderlyingDatatypePtr->size);
|
||||
}
|
||||
|
||||
If no :c:member:`~PyTypeObject.tp_repr` handler is specified, the interpreter will supply a
|
||||
|
|
@ -188,10 +193,11 @@ used instead.
|
|||
Here is a simple example::
|
||||
|
||||
static PyObject *
|
||||
newdatatype_str(newdatatypeobject *obj)
|
||||
newdatatype_str(PyObject *op)
|
||||
{
|
||||
newdatatypeobject *self = (newdatatypeobject *) op;
|
||||
return PyUnicode_FromFormat("Stringified_newdatatype{{size:%d}}",
|
||||
obj->obj_UnderlyingDatatypePtr->size);
|
||||
self->obj_UnderlyingDatatypePtr->size);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -329,16 +335,16 @@ method of a class would be called.
|
|||
Here is an example::
|
||||
|
||||
static PyObject *
|
||||
newdatatype_getattr(newdatatypeobject *obj, char *name)
|
||||
newdatatype_getattr(PyObject *op, char *name)
|
||||
{
|
||||
if (strcmp(name, "data") == 0)
|
||||
{
|
||||
return PyLong_FromLong(obj->data);
|
||||
newdatatypeobject *self = (newdatatypeobject *) op;
|
||||
if (strcmp(name, "data") == 0) {
|
||||
return PyLong_FromLong(self->data);
|
||||
}
|
||||
|
||||
PyErr_Format(PyExc_AttributeError,
|
||||
"'%.100s' object has no attribute '%.400s'",
|
||||
Py_TYPE(obj)->tp_name, name);
|
||||
Py_TYPE(self)->tp_name, name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -349,7 +355,7 @@ example that simply raises an exception; if this were really all you wanted, the
|
|||
:c:member:`~PyTypeObject.tp_setattr` handler should be set to ``NULL``. ::
|
||||
|
||||
static int
|
||||
newdatatype_setattr(newdatatypeobject *obj, char *name, PyObject *v)
|
||||
newdatatype_setattr(PyObject *op, char *name, PyObject *v)
|
||||
{
|
||||
PyErr_Format(PyExc_RuntimeError, "Read-only attribute: %s", name);
|
||||
return -1;
|
||||
|
|
@ -379,8 +385,10 @@ Here is a sample implementation, for a datatype that is considered equal if the
|
|||
size of an internal pointer is equal::
|
||||
|
||||
static PyObject *
|
||||
newdatatype_richcmp(newdatatypeobject *obj1, newdatatypeobject *obj2, int op)
|
||||
newdatatype_richcmp(PyObject *lhs, PyObject *rhs, int op)
|
||||
{
|
||||
newdatatypeobject *obj1 = (newdatatypeobject *) lhs;
|
||||
newdatatypeobject *obj2 = (newdatatypeobject *) rhs;
|
||||
PyObject *result;
|
||||
int c, size1, size2;
|
||||
|
||||
|
|
@ -399,8 +407,7 @@ size of an internal pointer is equal::
|
|||
case Py_GE: c = size1 >= size2; break;
|
||||
}
|
||||
result = c ? Py_True : Py_False;
|
||||
Py_INCREF(result);
|
||||
return result;
|
||||
return Py_NewRef(result);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -439,12 +446,14 @@ This function, if you choose to provide it, should return a hash number for an
|
|||
instance of your data type. Here is a simple example::
|
||||
|
||||
static Py_hash_t
|
||||
newdatatype_hash(newdatatypeobject *obj)
|
||||
newdatatype_hash(PyObject *op)
|
||||
{
|
||||
newdatatypeobject *self = (newdatatypeobject *) op;
|
||||
Py_hash_t result;
|
||||
result = obj->some_size + 32767 * obj->some_number;
|
||||
if (result == -1)
|
||||
result = -2;
|
||||
result = self->some_size + 32767 * self->some_number;
|
||||
if (result == -1) {
|
||||
result = -2;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -478,8 +487,9 @@ This function takes three arguments:
|
|||
Here is a toy ``tp_call`` implementation::
|
||||
|
||||
static PyObject *
|
||||
newdatatype_call(newdatatypeobject *obj, PyObject *args, PyObject *kwds)
|
||||
newdatatype_call(PyObject *op, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
newdatatypeobject *self = (newdatatypeobject *) op;
|
||||
PyObject *result;
|
||||
const char *arg1;
|
||||
const char *arg2;
|
||||
|
|
@ -490,7 +500,7 @@ Here is a toy ``tp_call`` implementation::
|
|||
}
|
||||
result = PyUnicode_FromFormat(
|
||||
"Returning -- value: [%d] arg1: [%s] arg2: [%s] arg3: [%s]\n",
|
||||
obj->obj_UnderlyingDatatypePtr->size,
|
||||
self->obj_UnderlyingDatatypePtr->size,
|
||||
arg1, arg2, arg3);
|
||||
return result;
|
||||
}
|
||||
|
|
@ -563,12 +573,12 @@ The only further addition is that ``tp_dealloc`` needs to clear any weak
|
|||
references (by calling :c:func:`PyObject_ClearWeakRefs`)::
|
||||
|
||||
static void
|
||||
Trivial_dealloc(TrivialObject *self)
|
||||
Trivial_dealloc(PyObject *op)
|
||||
{
|
||||
/* Clear weakrefs first before calling any destructors */
|
||||
PyObject_ClearWeakRefs((PyObject *) self);
|
||||
PyObject_ClearWeakRefs(op);
|
||||
/* ... remainder of destruction code omitted for brevity ... */
|
||||
Py_TYPE(self)->tp_free((PyObject *) self);
|
||||
Py_TYPE(op)->tp_free(op);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue